Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signer pt 1 #15702

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions accounts/abi/abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
"io"
"bytes"
)

// The ABI holds information about a contract's context and available
Expand Down Expand Up @@ -137,3 +138,14 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {

return nil
}

// methodById looks up a method by the 4-byte id
// returns nil if none found
func (abi *ABI) MethodById(sigdata []byte) *Method {
for _, method := range abi.Methods{
if bytes.Equal(method.Id(), sigdata) {
return &method
}
}
return nil
}
4 changes: 2 additions & 2 deletions accounts/abi/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (e Event) tupleUnpack(v interface{}, output []byte) error {
// need to move this up because they read sequentially
j += input.Type.Size
}
marshalledValue, err := toGoType((i+j)*32, input.Type, output)
marshalledValue, err := ToGoType((i+j)*32, input.Type, output)
if err != nil {
return err
}
Expand Down Expand Up @@ -126,7 +126,7 @@ func (e Event) singleUnpack(v interface{}, output []byte) error {

value := valueOf.Elem()

marshalledValue, err := toGoType(0, e.Inputs[0].Type, output)
marshalledValue, err := ToGoType(0, e.Inputs[0].Type, output)
if err != nil {
return err
}
Expand Down
4 changes: 2 additions & 2 deletions accounts/abi/method.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ func (method Method) tupleUnpack(v interface{}, output []byte) error {
// need to move this up because they read sequentially
j += toUnpack.Type.Size
}
marshalledValue, err := toGoType((i+j)*32, toUnpack.Type, output)
marshalledValue, err := ToGoType((i+j)*32, toUnpack.Type, output)
if err != nil {
return err
}
Expand Down Expand Up @@ -146,7 +146,7 @@ func (method Method) singleUnpack(v interface{}, output []byte) error {

value := valueOf.Elem()

marshalledValue, err := toGoType(0, method.Outputs[0].Type, output)
marshalledValue, err := ToGoType(0, method.Outputs[0].Type, output)
if err != nil {
return err
}
Expand Down
6 changes: 3 additions & 3 deletions accounts/abi/unpack.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
if t.Elem.T == ArrayTy && j != 0 {
i = start + t.Elem.Size*32*j
}
inter, err := toGoType(i, *t.Elem, output)
inter, err := ToGoType(i, *t.Elem, output)
if err != nil {
return nil, err
}
Expand All @@ -139,9 +139,9 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
return refSlice.Interface(), nil
}

// toGoType parses the output bytes and recursively assigns the value of these bytes
// ToGoType parses the output bytes and recursively assigns the value of these bytes
// into a go type with accordance with the ABI spec.
func toGoType(index int, t Type, output []byte) (interface{}, error) {
func ToGoType(index int, t Type, output []byte) (interface{}, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not happy about this change because you should be able to use

var v interface{}
abi.Unpack(&v, "method", calldata)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd be glad for some suggestions on how to do it 'properly' -- I did it the only way I saw possible. See https://github.com/holiman/go-ethereum/blob/signer_mhs/cmd/signer/abihelper.go#L65 for more context.

I didn't know you could decode into an interface{}, I thought you had to allocate a suitable struct beforehand. I'll try your suggestion.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That doesn't quite work, the Unpack function aims at decoding returndata, not inputs.
It panics at e.g.

	marshalledValue, err := ToGoType(0, method.Outputs[0].Type, output)

Because I'm throwing it input, and have not even defined any output. A hacky way to go about it would be to retranslate inputs to outputs in my abi, so that instead of supplying

{"type":"function","name":"send","inputs":[{"type":"uint256"}]}

I would supply

{"type":"function","name":"send","outputs":[{"type":"uint256"}]}

But is that nice?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's possible to make Unpack a bit more generic and handle inputs aswell as outputs... But I'm having some problems decoding 'tuple' types, though.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMHO we should make it work instead of exposing internals like toGoType.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. Let's try to clean up in the abi-swamp

if index+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
}
Expand Down
2 changes: 1 addition & 1 deletion accounts/usbwallet/hub.go
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ func (hub *Hub) refreshWallets() {
// breaking the Ledger protocol if that is waiting for user confirmation. This
// is a bug acknowledged at Ledger, but it won't be fixed on old devices so we
// need to prevent concurrent comms ourselves. The more elegant solution would
// be to ditch enumeration in favor of hutplug events, but that don't work yet
// be to ditch enumeration in favor of hotplug events, but that don't work yet
// on Windows so if we need to hack it anyway, this is more elegant for now.
hub.commsLock.Lock()
if hub.commsPend > 0 { // A confirmation is pending, don't refresh
Expand Down
2 changes: 1 addition & 1 deletion accounts/usbwallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ type wallet struct {
//
// As such, a hardware wallet needs two locks to function correctly. A state
// lock can be used to protect the wallet's software-side internal state, which
// must not be held exlusively during hardware communication. A communication
// must not be held exclusively during hardware communication. A communication
// lock can be used to achieve exclusive access to the device itself, this one
// however should allow "skipping" waiting for operations that might want to
// use the device, but can live without too (e.g. account self-derivation).
Expand Down
54 changes: 54 additions & 0 deletions common/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"math/rand"
"reflect"

"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto/sha3"
)
Expand Down Expand Up @@ -240,3 +241,56 @@ func (a *UnprefixedAddress) UnmarshalText(input []byte) error {
func (a UnprefixedAddress) MarshalText() ([]byte, error) {
return []byte(hex.EncodeToString(a[:])), nil
}

// MixedcaseAddress retains the original string, which may or may not be
// correctly checksummed
//
// TODO! Should we really keep both addr and original, or _only_ original, and
// always calculate addr on the fly? That would reduce the possibilities for errors
// if some caller modifies the original at some point. NB: If we do so, we should still
// parse the addr in UnmarshalJSON to ensure that the format is correct, e.g. correct size and
// hex-encoded and such
type MixedcaseAddress struct {
addr Address
original string
}
// NewMixedcaseAddress constructor (mainly for testing)
func NewMixedcaseAddress(addr Address) MixedcaseAddress {
return MixedcaseAddress{addr: addr, original: addr.Hex()}
}

// UnmarshalJSON parses MixedcaseAddress
func (ma *MixedcaseAddress) UnmarshalJSON(input []byte) error {
if err := hexutil.UnmarshalFixedJSON(addressT, input, ma.addr[:]); err != nil {
return err
}
return json.Unmarshal(input, &ma.original)
}

// MarshalJSON marshals the original value
func (ma *MixedcaseAddress) MarshalJSON() ([]byte, error) {
return json.Marshal(ma.original)
}

// Address returns the address
func (ma *MixedcaseAddress) Address() Address {
return ma.addr
}

// String implements fmt.Stringer
func (ma *MixedcaseAddress) String() string {
if ma.ValidChecksum() {
return fmt.Sprintf("%s [chksum ok]", ma.original)
}
return fmt.Sprintf("%s [chksum INVALID]", ma.original)
}

// ValidChecksum returns true if the address has valid checksum
func (ma *MixedcaseAddress) ValidChecksum() bool {
return ma.original == ma.addr.Hex()
}

// Original returns the mixed-case input string
func (ma *MixedcaseAddress) Original() string {
return ma.original
}
44 changes: 44 additions & 0 deletions common/types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package common

import (
"encoding/json"

"math/big"
"strings"
"testing"
Expand Down Expand Up @@ -149,3 +150,46 @@ func BenchmarkAddressHex(b *testing.B) {
testAddr.Hex()
}
}

func TestMixedcaseAccount_Address(t *testing.T) {

// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md
// Note: 0X{checksum_addr} is not valid according to spec above

var res []struct {
A MixedcaseAddress
Valid bool
}
if err := json.Unmarshal([]byte(`[
{"A" : "0xae967917c465db8578ca9024c205720b1a3651A9", "Valid": false},
{"A" : "0xAe967917c465db8578ca9024c205720b1a3651A9", "Valid": true},
{"A" : "0XAe967917c465db8578ca9024c205720b1a3651A9", "Valid": false},
{"A" : "0x1111111111111111111112222222222223333323", "Valid": true}
]`), &res); err != nil {
t.Fatal(err)
}

for _, r := range res {
if got := r.A.ValidChecksum(); got != r.Valid {
t.Errorf("Expected checksum %v, got checksum %v, input %v", r.Valid, got, r.A.String())
}
}

//These should throw exceptions:
var r2 []MixedcaseAddress
for _, r := range []string{
`["0x11111111111111111111122222222222233333"]`, // Too short
`["0x111111111111111111111222222222222333332"]`, // Too short
`["0x11111111111111111111122222222222233333234"]`, // Too long
`["0x111111111111111111111222222222222333332344"]`, // Too long
`["1111111111111111111112222222222223333323"]`, // Missing 0x
`["x1111111111111111111112222222222223333323"]`, // Missing 0
`["0xG111111111111111111112222222222223333323"]`, //Non-hex
} {
if err := json.Unmarshal([]byte(r), &r2); err == nil {
t.Errorf("Expected failure, input %v", r)
}

}

}
53 changes: 47 additions & 6 deletions rpc/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"time"

"github.com/ethereum/go-ethereum/log"
"os"
)

var (
Expand Down Expand Up @@ -171,20 +172,60 @@ func DialContext(ctx context.Context, rawurl string) (*Client, error) {
return DialHTTP(rawurl)
case "ws", "wss":
return DialWebsocket(ctx, rawurl, "")
case "stdio":
return DialStdIO(ctx)
case "":
return DialIPC(ctx, rawurl)
default:
return nil, fmt.Errorf("no known transport for URL scheme %q", u.Scheme)
}
}

type StdIOConn struct{}

func (io StdIOConn) Read(b []byte) (n int, err error) {
return os.Stdin.Read(b)
}

func (io StdIOConn) Write(b []byte) (n int, err error) {
return os.Stdout.Write(b)
}

func (io StdIOConn) Close() error {
return nil
}

func (io StdIOConn) LocalAddr() net.Addr {
return &net.UnixAddr{Name: "stdio", Net: "stdio"}
}

func (io StdIOConn) RemoteAddr() net.Addr {
return &net.UnixAddr{Name: "stdio", Net: "stdio"}
}

func (io StdIOConn) SetDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (io StdIOConn) SetReadDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}

func (io StdIOConn) SetWriteDeadline(t time.Time) error {
return &net.OpError{Op: "set", Net: "stdio", Source: nil, Addr: nil, Err: errors.New("deadline not supported")}
}
func DialStdIO(ctx context.Context) (*Client, error) {
return newClient(ctx, func(_ context.Context) (net.Conn, error) {
return StdIOConn{}, nil
})
}

func newClient(initctx context.Context, connectFunc func(context.Context) (net.Conn, error)) (*Client, error) {
conn, err := connectFunc(initctx)
if err != nil {
return nil, err
}
_, isHTTP := conn.(*httpConn)

c := &Client{
writeConn: conn,
isHTTP: isHTTP,
Expand Down Expand Up @@ -524,13 +565,13 @@ func (c *Client) dispatch(conn net.Conn) {
}

case err := <-c.readErr:
log.Debug(fmt.Sprintf("<-readErr: %v", err))
log.Debug("<-readErr", "err", err)
c.closeRequestOps(err)
conn.Close()
reading = false

case newconn := <-c.reconnected:
log.Debug(fmt.Sprintf("<-reconnected: (reading=%t) %v", reading, conn.RemoteAddr()))
log.Debug("<-reconnected", "reading", reading, "remote", conn.RemoteAddr())
if reading {
// Wait for the previous read loop to exit. This is a rare case.
conn.Close()
Expand Down Expand Up @@ -587,15 +628,15 @@ func (c *Client) closeRequestOps(err error) {

func (c *Client) handleNotification(msg *jsonrpcMessage) {
if !strings.HasSuffix(msg.Method, notificationMethodSuffix) {
log.Debug(fmt.Sprint("dropping non-subscription message: ", msg))
log.Debug("dropping non-subscription message", "msg", msg)
return
}
var subResult struct {
ID string `json:"subscription"`
Result json.RawMessage `json:"result"`
}
if err := json.Unmarshal(msg.Params, &subResult); err != nil {
log.Debug(fmt.Sprint("dropping invalid subscription message: ", msg))
log.Debug("dropping invalid subscription message", "msg", msg)
return
}
if c.subs[subResult.ID] != nil {
Expand All @@ -606,7 +647,7 @@ func (c *Client) handleNotification(msg *jsonrpcMessage) {
func (c *Client) handleResponse(msg *jsonrpcMessage) {
op := c.respWait[string(msg.ID)]
if op == nil {
log.Debug(fmt.Sprintf("unsolicited response %v", msg))
log.Debug("unsolicited response", "msg", msg)
return
}
delete(c.respWait, string(msg.ID))
Expand Down
7 changes: 6 additions & 1 deletion rpc/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,16 @@ func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// All checks passed, create a codec that reads direct from the request body
// untilEOF and writes the response to w and order the server to process a
// single request.
ctx := context.Background()
ctx = context.WithValue(ctx, "remote", r.RemoteAddr)
ctx = context.WithValue(ctx, "scheme", r.Proto)
ctx = context.WithValue(ctx, "local", r.Host)

codec := NewJSONCodec(&httpReadWriteNopCloser{r.Body, w})
defer codec.Close()

w.Header().Set("content-type", contentType)
srv.ServeSingleRequest(codec, OptionMethodInvocation)
srv.ServeSingleRequest(codec, OptionMethodInvocation, ctx)
}

// validateRequest returns a non-zero response code and error message if the
Expand Down
Loading