From 199bfbabeb9072d69e68b77807290407ef1bbf0e Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 14 May 2020 11:12:33 +0200 Subject: [PATCH 01/20] accounts/abi: refactored abi.Unpack --- accounts/abi/abi.go | 46 ++++++++++++++---- accounts/abi/abi_test.go | 16 +++---- accounts/abi/argument.go | 47 ++++++++++--------- accounts/abi/bind/base.go | 12 +++-- accounts/abi/bind/base_test.go | 7 ++- accounts/abi/event_test.go | 22 ++++----- accounts/abi/unpack_test.go | 6 +-- contracts/checkpointoracle/contract/oracle.go | 28 ++++------- mobile/bind.go | 18 ++----- 9 files changed, 106 insertions(+), 96 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 38196ed25c88..ee442e08c929 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -80,20 +80,50 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { return append(method.ID, arguments...), nil } -// Unpack output in v according to the abi specification. -func (abi ABI) Unpack(v interface{}, name string, data []byte) (err error) { +// Unpack unpacks the output according to the abi specification. +func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) { // since there can't be naming collisions with contracts and events, // we need to decide whether we're calling a method or an event + var args Arguments + if method, ok := abi.Methods[name]; ok { + if len(data)%32 != 0 { + return nil, fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data) + } + args = method.Outputs + } + if event, ok := abi.Events[name]; ok { + args = event.Inputs + } + if args == nil { + return nil, errors.New("abi: could not locate named method or event") + } + return args.Unpack(data) +} + +// UnpackIntoInterface unpacks the output in v according to the abi specification. +// It performs an additional copy. Please only use, if you want to unpack into a +// structure that not strictly confirms to the abi structure (e.g. has additional arguments) +func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error { + // since there can't be naming collisions with contracts and events, + // we need to decide whether we're calling a method or an event + var args Arguments if method, ok := abi.Methods[name]; ok { if len(data)%32 != 0 { return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data) } - return method.Outputs.Unpack(v, data) + args = method.Outputs } if event, ok := abi.Events[name]; ok { - return event.Inputs.Unpack(v, data) + args = event.Inputs } - return fmt.Errorf("abi: could not locate named method or event") + if args == nil { + return errors.New("abi: could not locate named method or event") + } + unpacked, err := args.Unpack(data) + if err != nil { + return err + } + return args.Copy(v, unpacked) } // UnpackIntoMap unpacks a log into the provided map[string]interface{}. @@ -250,10 +280,10 @@ func UnpackRevert(data []byte) (string, error) { if !bytes.Equal(data[:4], revertSelector) { return "", errors.New("invalid data for unpacking") } - var reason string typ, _ := NewType("string", "", nil) - if err := (Arguments{{Type: typ}}).Unpack(&reason, data[4:]); err != nil { + unpacked, err := (Arguments{{Type: typ}}).Unpack(data[4:]) + if err != nil { return "", err } - return reason, nil + return unpacked[0].(string), nil } diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index f7d7f7aa620f..7fa9d499a21f 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -181,18 +181,14 @@ func TestConstructor(t *testing.T) { if err != nil { t.Error(err) } - v := struct { - A *big.Int - B *big.Int - }{new(big.Int), new(big.Int)} - //abi.Unpack(&v, "", packed) - if err := abi.Constructor.Inputs.Unpack(&v, packed); err != nil { + unpacked, err := abi.Constructor.Inputs.Unpack(packed) + if err != nil { t.Error(err) } - if !reflect.DeepEqual(v.A, big.NewInt(1)) { + if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) { t.Error("Unable to pack/unpack from constructor") } - if !reflect.DeepEqual(v.B, big.NewInt(2)) { + if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) { t.Error("Unable to pack/unpack from constructor") } } @@ -743,7 +739,7 @@ func TestUnpackEvent(t *testing.T) { } var ev ReceivedEvent - err = abi.Unpack(&ev, "received", data) + err = abi.UnpackIntoInterface(&ev, "received", data) if err != nil { t.Error(err) } @@ -752,7 +748,7 @@ func TestUnpackEvent(t *testing.T) { Sender common.Address } var receivedAddrEv ReceivedAddrEvent - err = abi.Unpack(&receivedAddrEv, "receivedAddr", data) + err = abi.UnpackIntoInterface(&receivedAddrEv, "receivedAddr", data) if err != nil { t.Error(err) } diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index aaef3bd41cdf..65216b9a4c74 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -76,28 +76,15 @@ func (arguments Arguments) isTuple() bool { } // Unpack performs the operation hexdata -> Go format. -func (arguments Arguments) Unpack(v interface{}, data []byte) error { +func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { if len(data) == 0 { if len(arguments) != 0 { - return fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") + return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") } - return nil // Nothing to unmarshal, return - } - // make sure the passed value is arguments pointer - if reflect.Ptr != reflect.ValueOf(v).Kind() { - return fmt.Errorf("abi: Unpack(non-pointer %T)", v) - } - marshalledValues, err := arguments.UnpackValues(data) - if err != nil { - return err - } - if len(marshalledValues) == 0 { - return fmt.Errorf("abi: Unpack(no-values unmarshalled %T)", v) - } - if arguments.isTuple() { - return arguments.unpackTuple(v, marshalledValues) + return make([]interface{}, 0, len(arguments.NonIndexed())), nil // Nothing to unmarshal, return } - return arguments.unpackAtomic(v, marshalledValues[0]) + + return arguments.UnpackValues(data) } // UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value. @@ -122,8 +109,26 @@ func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) return nil } -// unpackAtomic unpacks ( hexdata -> go ) a single value. -func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error { +// Copy performs the operation go format -> provided struct. +func (arguments Arguments) Copy(v interface{}, values []interface{}) error { + // make sure the passed value is arguments pointer + if reflect.Ptr != reflect.ValueOf(v).Kind() { + return fmt.Errorf("abi: Unpack(non-pointer %T)", v) + } + if len(values) == 0 { + if len(arguments) != 0 { + return fmt.Errorf("abi: attempting to copy no values while %d arguments are expected", len(arguments)) + } + return nil // Nothing to copy, return + } + if arguments.isTuple() { + return arguments.copyTuple(v, values) + } + return arguments.copyAtomic(v, values[0]) +} + +// unpackAtomic unpacks ( hexdata -> go ) a single value +func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{}) error { dst := reflect.ValueOf(v).Elem() src := reflect.ValueOf(marshalledValues) @@ -134,7 +139,7 @@ func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interfac } // unpackTuple unpacks ( hexdata -> go ) a batch of values. -func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error { +func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface{}) error { value := reflect.ValueOf(v).Elem() nonIndexedArgs := arguments.NonIndexed() diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index d9935e3de726..5c3fd13c6334 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -117,7 +117,7 @@ func DeployContract(opts *TransactOpts, abi abi.ABI, bytecode []byte, backend Co // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, params ...interface{}) error { +func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method string, params ...interface{}) error { // Don't crash on a lazy user if opts == nil { opts = new(CallOpts) @@ -158,10 +158,14 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string, } } } - if err != nil { + + if results == nil || len(*results) == 0 { + res, err := c.abi.Unpack(method, output) + results = &res return err } - return c.abi.Unpack(result, method, output) + res := *results + return c.abi.UnpackIntoInterface(res[0], method, output) } // Transact invokes the (paid) contract method with params as input values. @@ -339,7 +343,7 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter // UnpackLog unpacks a retrieved log into the provided output structure. func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error { if len(log.Data) > 0 { - if err := c.abi.Unpack(out, event, log.Data); err != nil { + if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil { return err } } diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go index 7d287850f44b..c4740f68b750 100644 --- a/accounts/abi/bind/base_test.go +++ b/accounts/abi/bind/base_test.go @@ -71,11 +71,10 @@ func TestPassingBlockNumber(t *testing.T) { }, }, }, mc, nil, nil) - var ret string blockNumber := big.NewInt(42) - bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, &ret, "something") + bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something") if mc.callContractBlockNumber != blockNumber { t.Fatalf("CallContract() was not passed the block number") @@ -85,7 +84,7 @@ func TestPassingBlockNumber(t *testing.T) { t.Fatalf("CodeAt() was not passed the block number") } - bc.Call(&bind.CallOpts{}, &ret, "something") + bc.Call(&bind.CallOpts{}, nil, "something") if mc.callContractBlockNumber != nil { t.Fatalf("CallContract() was passed a block number when it should not have been") @@ -95,7 +94,7 @@ func TestPassingBlockNumber(t *testing.T) { t.Fatalf("CodeAt() was passed a block number when it should not have been") } - bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, &ret, "something") + bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something") if !mc.pendingCallContractCalled { t.Fatalf("CallContract() was not passed the block number") diff --git a/accounts/abi/event_test.go b/accounts/abi/event_test.go index 79504c28ce3f..3332f8a07216 100644 --- a/accounts/abi/event_test.go +++ b/accounts/abi/event_test.go @@ -147,10 +147,6 @@ func TestEventString(t *testing.T) { // TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array. func TestEventMultiValueWithArrayUnpack(t *testing.T) { definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]` - type testStruct struct { - Value1 [2]uint8 - Value2 uint8 - } abi, err := JSON(strings.NewReader(definition)) require.NoError(t, err) var b bytes.Buffer @@ -158,10 +154,10 @@ func TestEventMultiValueWithArrayUnpack(t *testing.T) { for ; i <= 3; i++ { b.Write(packNum(reflect.ValueOf(i))) } - var rst testStruct - require.NoError(t, abi.Unpack(&rst, "test", b.Bytes())) - require.Equal(t, [2]uint8{1, 2}, rst.Value1) - require.Equal(t, uint8(3), rst.Value2) + unpacked, err := abi.Unpack("test", b.Bytes()) + require.NoError(t, err) + require.Equal(t, [2]uint8{1, 2}, unpacked[0]) + require.Equal(t, uint8(3), unpacked[1]) } func TestEventTupleUnpack(t *testing.T) { @@ -351,14 +347,14 @@ func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, ass var e Event assert.NoError(json.Unmarshal(jsonEvent, &e), "Should be able to unmarshal event ABI") a := ABI{Events: map[string]Event{"e": e}} - return a.Unpack(dest, "e", data) + return a.UnpackIntoInterface(dest, "e", data) } // TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder. func TestEventUnpackIndexed(t *testing.T) { definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]` type testStruct struct { - Value1 uint8 + Value1 uint8 // indexed Value2 uint8 } abi, err := JSON(strings.NewReader(definition)) @@ -366,7 +362,7 @@ func TestEventUnpackIndexed(t *testing.T) { var b bytes.Buffer b.Write(packNum(reflect.ValueOf(uint8(8)))) var rst testStruct - require.NoError(t, abi.Unpack(&rst, "test", b.Bytes())) + require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes())) require.Equal(t, uint8(0), rst.Value1) require.Equal(t, uint8(8), rst.Value2) } @@ -375,7 +371,7 @@ func TestEventUnpackIndexed(t *testing.T) { func TestEventIndexedWithArrayUnpack(t *testing.T) { definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"string"}]}]` type testStruct struct { - Value1 [2]uint8 + Value1 [2]uint8 // indexed Value2 string } abi, err := JSON(strings.NewReader(definition)) @@ -388,7 +384,7 @@ func TestEventIndexedWithArrayUnpack(t *testing.T) { b.Write(common.RightPadBytes([]byte(stringOut), 32)) var rst testStruct - require.NoError(t, abi.Unpack(&rst, "test", b.Bytes())) + require.NoError(t, abi.UnpackIntoInterface(&rst, "test", b.Bytes())) require.Equal(t, [2]uint8{0, 0}, rst.Value1) require.Equal(t, stringOut, rst.Value2) } diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index 767d1540e60c..f6f77d818b9e 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -16,6 +16,7 @@ package abi +/* import ( "bytes" "encoding/hex" @@ -44,13 +45,11 @@ func TestUnpack(t *testing.T) { if err != nil { t.Fatalf("invalid hex %s: %v", test.packed, err) } - outptr := reflect.New(reflect.TypeOf(test.unpacked)) - err = abi.Unpack(outptr.Interface(), "method", encb) + out, err := abi.Unpack("method", encb) if err != nil { t.Errorf("test %d (%v) failed: %v", i, test.def, err) return } - out := outptr.Elem().Interface() if !reflect.DeepEqual(test.unpacked, out) { t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out) } @@ -919,3 +918,4 @@ func TestOOMMaliciousInput(t *testing.T) { } } } +*/ diff --git a/contracts/checkpointoracle/contract/oracle.go b/contracts/checkpointoracle/contract/oracle.go index 998ccb93c264..0f9b8afc9878 100644 --- a/contracts/checkpointoracle/contract/oracle.go +++ b/contracts/checkpointoracle/contract/oracle.go @@ -153,7 +153,7 @@ func bindCheckpointOracle(address common.Address, caller bind.ContractCaller, tr // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_CheckpointOracle *CheckpointOracleRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_CheckpointOracle *CheckpointOracleRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _CheckpointOracle.Contract.CheckpointOracleCaller.contract.Call(opts, result, method, params...) } @@ -172,7 +172,7 @@ func (_CheckpointOracle *CheckpointOracleRaw) Transact(opts *bind.TransactOpts, // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. -func (_CheckpointOracle *CheckpointOracleCallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { +func (_CheckpointOracle *CheckpointOracleCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _CheckpointOracle.Contract.contract.Call(opts, result, method, params...) } @@ -191,12 +191,9 @@ func (_CheckpointOracle *CheckpointOracleTransactorRaw) Transact(opts *bind.Tran // // Solidity: function GetAllAdmin() constant returns(address[]) func (_CheckpointOracle *CheckpointOracleCaller) GetAllAdmin(opts *bind.CallOpts) ([]common.Address, error) { - var ( - ret0 = new([]common.Address) - ) - out := ret0 - err := _CheckpointOracle.contract.Call(opts, out, "GetAllAdmin") - return *ret0, err + var out []interface{} + err := _CheckpointOracle.contract.Call(opts, &out, "GetAllAdmin") + return out[0].([]common.Address), err } // GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc. @@ -217,18 +214,9 @@ func (_CheckpointOracle *CheckpointOracleCallerSession) GetAllAdmin() ([]common. // // Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256) func (_CheckpointOracle *CheckpointOracleCaller) GetLatestCheckpoint(opts *bind.CallOpts) (uint64, [32]byte, *big.Int, error) { - var ( - ret0 = new(uint64) - ret1 = new([32]byte) - ret2 = new(*big.Int) - ) - out := &[]interface{}{ - ret0, - ret1, - ret2, - } - err := _CheckpointOracle.contract.Call(opts, out, "GetLatestCheckpoint") - return *ret0, *ret1, *ret2, err + var out []interface{} + err := _CheckpointOracle.contract.Call(opts, &out, "GetLatestCheckpoint") + return out[0].(uint64), out[1].([32]byte), out[2].(*big.Int), err } // GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c. diff --git a/mobile/bind.go b/mobile/bind.go index f64b37ec1580..afa97b538221 100644 --- a/mobile/bind.go +++ b/mobile/bind.go @@ -171,20 +171,12 @@ func (c *BoundContract) GetDeployer() *Transaction { // Call invokes the (constant) contract method with params as input values and // sets the output to result. func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error { - if len(out.objects) == 1 { - result := out.objects[0] - if err := c.contract.Call(&opts.opts, result, method, args.objects...); err != nil { - return err - } - out.objects[0] = result - } else { - results := make([]interface{}, len(out.objects)) - copy(results, out.objects) - if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil { - return err - } - copy(out.objects, results) + results := make([]interface{}, len(out.objects)) + copy(results, out.objects) + if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil { + return err } + copy(out.objects, results) return nil } From 9b90470829086ada79cc8f496847d39d4113f325 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 15 May 2020 12:10:21 +0200 Subject: [PATCH 02/20] accounts/abi/bind: fixed error --- accounts/abi/bind/base.go | 2 +- contracts/checkpointoracle/contract/oracle.go | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 5c3fd13c6334..a24a3fd892cc 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -161,7 +161,7 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri if results == nil || len(*results) == 0 { res, err := c.abi.Unpack(method, output) - results = &res + *results = res return err } res := *results diff --git a/contracts/checkpointoracle/contract/oracle.go b/contracts/checkpointoracle/contract/oracle.go index 0f9b8afc9878..fc6795bab27d 100644 --- a/contracts/checkpointoracle/contract/oracle.go +++ b/contracts/checkpointoracle/contract/oracle.go @@ -216,6 +216,9 @@ func (_CheckpointOracle *CheckpointOracleCallerSession) GetAllAdmin() ([]common. func (_CheckpointOracle *CheckpointOracleCaller) GetLatestCheckpoint(opts *bind.CallOpts) (uint64, [32]byte, *big.Int, error) { var out []interface{} err := _CheckpointOracle.contract.Call(opts, &out, "GetLatestCheckpoint") + if err != nil || len(out) != 3 { + return uint64(0), [32]byte{}, nil, err + } return out[0].(uint64), out[1].([32]byte), out[2].(*big.Int), err } From 8946e6ec19c898ab9ff21c906738303bb5b25f5d Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 28 May 2020 14:51:22 +0200 Subject: [PATCH 03/20] accounts/abi/bind: modified template --- accounts/abi/argument.go | 16 +++++++++++++--- accounts/abi/bind/bind_test.go | 4 ++-- accounts/abi/bind/template.go | 30 +++++++++++++++--------------- 3 files changed, 30 insertions(+), 20 deletions(-) diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index 65216b9a4c74..9886d77dc9de 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -79,14 +79,24 @@ func (arguments Arguments) isTuple() bool { func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { if len(data) == 0 { if len(arguments) != 0 { - return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") + return arguments.makeDefaulArgs(), fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") } - return make([]interface{}, 0, len(arguments.NonIndexed())), nil // Nothing to unmarshal, return + // Nothing to unmarshal, return default arguments + return arguments.makeDefaulArgs(), nil } return arguments.UnpackValues(data) } +func (arguments Arguments) makeDefaulArgs() []interface{} { + nonIndexedArgs := arguments.NonIndexed() + retval := make([]interface{}, len(nonIndexedArgs)) + for index, arg := range nonIndexedArgs { + retval[index] = reflect.New(arg.Type.getType()) + } + return retval +} + // UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value. func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error { // Make sure map is not nil @@ -205,7 +215,7 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) { virtualArgs += getTypeSize(arg.Type)/32 - 1 } if err != nil { - return nil, err + return arguments.makeDefaulArgs(), err } retval = append(retval, marshalledValue) } diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go index a5f08499d228..8bfbf30b53d5 100644 --- a/accounts/abi/bind/bind_test.go +++ b/accounts/abi/bind/bind_test.go @@ -1696,11 +1696,11 @@ func TestGolangBindings(t *testing.T) { t.Skip("go sdk not found for testing") } // Create a temporary workspace for the test suite - ws, err := ioutil.TempDir("", "") + ws, err := ioutil.TempDir("", "binding-test") if err != nil { t.Fatalf("failed to create temporary workspace: %v", err) } - defer os.RemoveAll(ws) + //defer os.RemoveAll(ws) pkg := filepath.Join(ws, "bindtest") if err = os.MkdirAll(pkg, 0700); err != nil { diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index f19f1315ae88..35e8b6c12926 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -261,7 +261,7 @@ var ( // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. - func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + func (_{{$contract.Type}} *{{$contract.Type}}Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _{{$contract.Type}}.Contract.{{$contract.Type}}Caller.contract.Call(opts, result, method, params...) } @@ -280,7 +280,7 @@ var ( // sets the output to result. The result type might be a single field for simple // returns, a slice of interfaces for anonymous returns and a struct for named // returns. - func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result interface{}, method string, params ...interface{}) error { + func (_{{$contract.Type}} *{{$contract.Type}}CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { return _{{$contract.Type}}.Contract.contract.Call(opts, result, method, params...) } @@ -300,19 +300,19 @@ var ( // // Solidity: {{.Original.String}} func (_{{$contract.Type}} *{{$contract.Type}}Caller) {{.Normalized.Name}}(opts *bind.CallOpts {{range .Normalized.Inputs}}, {{.Name}} {{bindtype .Type $structs}} {{end}}) ({{if .Structured}}struct{ {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}};{{end}} },{{else}}{{range .Normalized.Outputs}}{{bindtype .Type $structs}},{{end}}{{end}} error) { - {{if .Structured}}ret := new(struct{ - {{range .Normalized.Outputs}}{{.Name}} {{bindtype .Type $structs}} - {{end}} - }){{else}}var ( - {{range $i, $_ := .Normalized.Outputs}}ret{{$i}} = new({{bindtype .Type $structs}}) - {{end}} - ){{end}} - out := {{if .Structured}}ret{{else}}{{if eq (len .Normalized.Outputs) 1}}ret0{{else}}&[]interface{}{ - {{range $i, $_ := .Normalized.Outputs}}ret{{$i}}, - {{end}} - }{{end}}{{end}} - err := _{{$contract.Type}}.contract.Call(opts, out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) - return {{if .Structured}}*ret,{{else}}{{range $i, $_ := .Normalized.Outputs}}*ret{{$i}},{{end}}{{end}} err + var out []interface{} + err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) + {{if .Structured}} + outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} }) + {{range $i, $t := .Normalized.Outputs}} + outstruct.{{.Name}} = out[{{$i}}].({{bindtype .Type $structs}}){{end}} + {{else}} + if err != nil { + return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err + } + {{end}} + + return {{if .Structured}}*outstruct,{{else}}{{range $i, $t := .Normalized.Outputs}}out[{{$i}}].({{bindtype .Type $structs}}),{{end}}{{end}} err } // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}. From 5006a5110e0a03630dce8348f4b16a9dd721620f Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 8 Jun 2020 19:32:25 +0200 Subject: [PATCH 04/20] accounts/abi/bind: added ToStruct for conversion --- accounts/abi/bind/bind.go | 16 ++++++++++++++++ accounts/abi/bind/template.go | 2 +- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index 0e98709b1455..fd37a7d9d6c3 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -25,6 +25,7 @@ import ( "errors" "fmt" "go/format" + "reflect" "regexp" "strings" "text/template" @@ -584,3 +585,18 @@ func hasStruct(t abi.Type) bool { return false } } + +// ToStruct converts a interface of a runtime type into a interface of the +// given type +// e.g. turn +// var fields []reflect.StructField +// fields = append(fields, reflect.StructField{ +// Name: "X", +// Type: reflect.TypeOf(new(big.Int)), +// Tag: reflect.StructTag("json:\"" + "x" + "\""), +// } +// into +// type TupleT struct { X *big.Int } +func ToStruct(in interface{}, typ interface{}) interface{} { + return reflect.ValueOf(in).Convert(reflect.TypeOf(typ)).Interface() +} diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index 35e8b6c12926..aeba5731d1a7 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -312,7 +312,7 @@ var ( } {{end}} - return {{if .Structured}}*outstruct,{{else}}{{range $i, $t := .Normalized.Outputs}}out[{{$i}}].({{bindtype .Type $structs}}),{{end}}{{end}} err + return {{if .Structured}}*outstruct,{{else}}{{range $i, $t := .Normalized.Outputs}}bind.ToStruct(out[{{$i}}], *new({{bindtype .Type $structs}})).({{bindtype .Type $structs}}),{{end}}{{end}} err } // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}. From c2e7250eb146ddd29c0e5ff9dd203db3088b7e73 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 16 Jun 2020 10:43:13 +0200 Subject: [PATCH 05/20] accounts/abi: reenabled tests --- accounts/abi/packing_test.go | 8 +++--- accounts/abi/unpack_test.go | 56 +++++++++++++++++------------------- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/accounts/abi/packing_test.go b/accounts/abi/packing_test.go index 16b4dc43d75d..bb9d7cf0ff0c 100644 --- a/accounts/abi/packing_test.go +++ b/accounts/abi/packing_test.go @@ -620,7 +620,7 @@ var packUnpackTests = []packUnpackTest{ { def: `[{"type": "bytes32[]"}]`, - unpacked: []common.Hash{{1}, {2}}, + unpacked: [][32]byte{{1}, {2}}, packed: "0000000000000000000000000000000000000000000000000000000000000020" + "0000000000000000000000000000000000000000000000000000000000000002" + "0100000000000000000000000000000000000000000000000000000000000000" + @@ -722,7 +722,7 @@ var packUnpackTests = []packUnpackTest{ }, // struct outputs { - def: `[{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}]`, + def: `["components": [{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}]]`, packed: "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002", unpacked: struct { @@ -855,7 +855,7 @@ var packUnpackTests = []packUnpackTest{ "0500000000000000000000000000000000000000000000000000000000000000", // struct[e] array[1][2] }, { - def: `[{"name":"a","type":"string"}, + def: `"type": "tuple","components": [{"name":"a","type":"string"}, {"name":"b","type":"int64"}, {"name":"c","type":"bytes"}, {"name":"d","type":"string[]"}, @@ -894,7 +894,7 @@ var packUnpackTests = []packUnpackTest{ "0000000000000000000000000200000000000000000000000000000000000000", // common.Address{2} }, { - def: `[{"components": [{"name": "a","type": "uint256"}, + def: `[{"type": "tuple","components": [{"name": "a","type": "uint256"}, {"name": "b","type": "uint256[]"}], "name": "a","type": "tuple"}, {"name": "b","type": "uint256[]"}]`, diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index f6f77d818b9e..182c222c0400 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -16,7 +16,6 @@ package abi -/* import ( "bytes" "encoding/hex" @@ -50,7 +49,7 @@ func TestUnpack(t *testing.T) { t.Errorf("test %d (%v) failed: %v", i, test.def, err) return } - if !reflect.DeepEqual(test.unpacked, out) { + if !reflect.DeepEqual(test.unpacked, out[0]) { t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out) } }) @@ -220,7 +219,7 @@ func TestLocalUnpackTests(t *testing.T) { t.Fatalf("invalid hex %s: %v", test.enc, err) } outptr := reflect.New(reflect.TypeOf(test.want)) - err = abi.Unpack(outptr.Interface(), "method", encb) + err = abi.UnpackIntoInterface(outptr.Interface(), "method", encb) if err := test.checkError(err); err != nil { t.Errorf("test %d (%v) failed: %v", i, test.def, err) return @@ -233,7 +232,7 @@ func TestLocalUnpackTests(t *testing.T) { } } -func TestUnpackSetDynamicArrayOutput(t *testing.T) { +func TestUnpackIntoInterfaceSetDynamicArrayOutput(t *testing.T) { abi, err := JSON(strings.NewReader(`[{"constant":true,"inputs":[],"name":"testDynamicFixedBytes15","outputs":[{"name":"","type":"bytes15[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"testDynamicFixedBytes32","outputs":[{"name":"","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"}]`)) if err != nil { t.Fatal(err) @@ -248,7 +247,7 @@ func TestUnpackSetDynamicArrayOutput(t *testing.T) { ) // test 32 - err = abi.Unpack(&out32, "testDynamicFixedBytes32", marshalledReturn32) + err = abi.UnpackIntoInterface(&out32, "testDynamicFixedBytes32", marshalledReturn32) if err != nil { t.Fatal(err) } @@ -265,7 +264,7 @@ func TestUnpackSetDynamicArrayOutput(t *testing.T) { } // test 15 - err = abi.Unpack(&out15, "testDynamicFixedBytes32", marshalledReturn15) + err = abi.UnpackIntoInterface(&out15, "testDynamicFixedBytes32", marshalledReturn15) if err != nil { t.Fatal(err) } @@ -366,7 +365,7 @@ func TestMethodMultiReturn(t *testing.T) { tc := tc t.Run(tc.name, func(t *testing.T) { require := require.New(t) - err := abi.Unpack(tc.dest, "multi", data) + err := abi.UnpackIntoInterface(tc.dest, "multi", data) if tc.error == "" { require.Nil(err, "Should be able to unpack method outputs.") require.Equal(tc.expected, tc.dest) @@ -389,7 +388,7 @@ func TestMultiReturnWithArray(t *testing.T) { ret1, ret1Exp := new([3]uint64), [3]uint64{9, 9, 9} ret2, ret2Exp := new(uint64), uint64(8) - if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { + if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { t.Fatal(err) } if !reflect.DeepEqual(*ret1, ret1Exp) { @@ -413,7 +412,7 @@ func TestMultiReturnWithStringArray(t *testing.T) { ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f") ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"} ret4, ret4Exp := new(bool), false - if err := abi.Unpack(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil { + if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil { t.Fatal(err) } if !reflect.DeepEqual(*ret1, ret1Exp) { @@ -451,7 +450,7 @@ func TestMultiReturnWithStringSlice(t *testing.T) { buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"} ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)} - if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { + if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { t.Fatal(err) } if !reflect.DeepEqual(*ret1, ret1Exp) { @@ -491,7 +490,7 @@ func TestMultiReturnWithDeeplyNestedArray(t *testing.T) { {{0x411, 0x412, 0x413}, {0x421, 0x422, 0x423}}, } ret2, ret2Exp := new(uint64), uint64(0x9876) - if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { + if err := abi.UnpackIntoInterface(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil { t.Fatal(err) } if !reflect.DeepEqual(*ret1, ret1Exp) { @@ -530,7 +529,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000a")) buff.Write(common.Hex2Bytes("0102000000000000000000000000000000000000000000000000000000000000")) - err = abi.Unpack(&mixedBytes, "mixedBytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&mixedBytes, "mixedBytes", buff.Bytes()) if err != nil { t.Error(err) } else { @@ -545,7 +544,7 @@ func TestUnmarshal(t *testing.T) { // marshal int var Int *big.Int - err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + err = abi.UnpackIntoInterface(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) if err != nil { t.Error(err) } @@ -556,7 +555,7 @@ func TestUnmarshal(t *testing.T) { // marshal bool var Bool bool - err = abi.Unpack(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) + err = abi.UnpackIntoInterface(&Bool, "bool", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) if err != nil { t.Error(err) } @@ -573,7 +572,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(bytesOut) var Bytes []byte - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -589,7 +588,7 @@ func TestUnmarshal(t *testing.T) { bytesOut = common.RightPadBytes([]byte("hello"), 64) buff.Write(bytesOut) - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -605,7 +604,7 @@ func TestUnmarshal(t *testing.T) { bytesOut = common.RightPadBytes([]byte("hello"), 64) buff.Write(bytesOut) - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -615,7 +614,7 @@ func TestUnmarshal(t *testing.T) { } // marshal dynamic bytes output empty - err = abi.Unpack(&Bytes, "bytes", nil) + err = abi.UnpackIntoInterface(&Bytes, "bytes", nil) if err == nil { t.Error("expected error") } @@ -626,7 +625,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005")) buff.Write(common.RightPadBytes([]byte("hello"), 32)) - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err != nil { t.Error(err) } @@ -640,7 +639,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.RightPadBytes([]byte("hello"), 32)) var hash common.Hash - err = abi.Unpack(&hash, "fixed", buff.Bytes()) + err = abi.UnpackIntoInterface(&hash, "fixed", buff.Bytes()) if err != nil { t.Error(err) } @@ -653,12 +652,12 @@ func TestUnmarshal(t *testing.T) { // marshal error buff.Reset() buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) - err = abi.Unpack(&Bytes, "bytes", buff.Bytes()) + err = abi.UnpackIntoInterface(&Bytes, "bytes", buff.Bytes()) if err == nil { t.Error("expected error") } - err = abi.Unpack(&Bytes, "multi", make([]byte, 64)) + err = abi.UnpackIntoInterface(&Bytes, "multi", make([]byte, 64)) if err == nil { t.Error("expected error") } @@ -669,7 +668,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000003")) // marshal int array var intArray [3]*big.Int - err = abi.Unpack(&intArray, "intArraySingle", buff.Bytes()) + err = abi.UnpackIntoInterface(&intArray, "intArraySingle", buff.Bytes()) if err != nil { t.Error(err) } @@ -690,7 +689,7 @@ func TestUnmarshal(t *testing.T) { buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000")) var outAddr []common.Address - err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes()) + err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes()) if err != nil { t.Fatal("didn't expect error:", err) } @@ -717,7 +716,7 @@ func TestUnmarshal(t *testing.T) { A []common.Address B []common.Address } - err = abi.Unpack(&outAddrStruct, "addressSliceDouble", buff.Bytes()) + err = abi.UnpackIntoInterface(&outAddrStruct, "addressSliceDouble", buff.Bytes()) if err != nil { t.Fatal("didn't expect error:", err) } @@ -745,7 +744,7 @@ func TestUnmarshal(t *testing.T) { buff.Reset() buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100")) - err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes()) + err = abi.UnpackIntoInterface(&outAddr, "addressSliceSingle", buff.Bytes()) if err == nil { t.Fatal("expected error:", err) } @@ -768,7 +767,7 @@ func TestUnpackTuple(t *testing.T) { B *big.Int }{new(big.Int), new(big.Int)} - err = abi.Unpack(&v, "tuple", buff.Bytes()) + err = abi.UnpackIntoInterface(&v, "tuple", buff.Bytes()) if err != nil { t.Error(err) } else { @@ -840,7 +839,7 @@ func TestUnpackTuple(t *testing.T) { A: big.NewInt(1), } - err = abi.Unpack(&ret, "tuple", buff.Bytes()) + err = abi.UnpackIntoInterface(&ret, "tuple", buff.Bytes()) if err != nil { t.Error(err) } @@ -918,4 +917,3 @@ func TestOOMMaliciousInput(t *testing.T) { } } } -*/ From a2e0db2085b1597a24ebe3eab1da0eb16113cb68 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 16 Jun 2020 14:52:21 +0200 Subject: [PATCH 06/20] accounts/abi: fixed tests --- accounts/abi/abi.go | 2 +- accounts/abi/argument.go | 3 +-- accounts/abi/pack_test.go | 13 +------------ accounts/abi/packing_test.go | 22 +++++++++++----------- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index ee442e08c929..5a07f2fb972a 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -102,7 +102,7 @@ func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) { // UnpackIntoInterface unpacks the output in v according to the abi specification. // It performs an additional copy. Please only use, if you want to unpack into a -// structure that not strictly confirms to the abi structure (e.g. has additional arguments) +// structure that does not strictly confirm to the abi structure (e.g. has additional arguments) func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error { // since there can't be naming collisions with contracts and events, // we need to decide whether we're calling a method or an event diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index 9886d77dc9de..f4c19ef2e7ca 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -84,7 +84,6 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { // Nothing to unmarshal, return default arguments return arguments.makeDefaulArgs(), nil } - return arguments.UnpackValues(data) } @@ -148,7 +147,7 @@ func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{ return set(dst, src) } -// unpackTuple unpacks ( hexdata -> go ) a batch of values. +// copyTuple copies a batch of values from marshalledValues to v. func (arguments Arguments) copyTuple(v interface{}, marshalledValues []interface{}) error { value := reflect.ValueOf(v).Elem() nonIndexedArgs := arguments.NonIndexed() diff --git a/accounts/abi/pack_test.go b/accounts/abi/pack_test.go index 284215a7d7b4..5c7cb1cc1a24 100644 --- a/accounts/abi/pack_test.go +++ b/accounts/abi/pack_test.go @@ -44,18 +44,7 @@ func TestPack(t *testing.T) { t.Fatalf("invalid ABI definition %s, %v", inDef, err) } var packed []byte - if reflect.TypeOf(test.unpacked).Kind() != reflect.Struct { - packed, err = inAbi.Pack("method", test.unpacked) - } else { - // if want is a struct we need to use the components. - elem := reflect.ValueOf(test.unpacked) - var values []interface{} - for i := 0; i < elem.NumField(); i++ { - field := elem.Field(i) - values = append(values, field.Interface()) - } - packed, err = inAbi.Pack("method", values...) - } + packed, err = inAbi.Pack("method", test.unpacked) if err != nil { t.Fatalf("test %d (%v) failed: %v", i, test.def, err) diff --git a/accounts/abi/packing_test.go b/accounts/abi/packing_test.go index bb9d7cf0ff0c..8260511690c5 100644 --- a/accounts/abi/packing_test.go +++ b/accounts/abi/packing_test.go @@ -722,7 +722,7 @@ var packUnpackTests = []packUnpackTest{ }, // struct outputs { - def: `["components": [{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}]]`, + def: `[{"components": [{"name":"int1","type":"int256"},{"name":"int2","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002", unpacked: struct { @@ -731,28 +731,28 @@ var packUnpackTests = []packUnpackTest{ }{big.NewInt(1), big.NewInt(2)}, }, { - def: `[{"name":"int_one","type":"int256"}]`, + def: `[{"components": [{"name":"int_one","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001", unpacked: struct { IntOne *big.Int }{big.NewInt(1)}, }, { - def: `[{"name":"int__one","type":"int256"}]`, + def: `[{"components": [{"name":"int__one","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001", unpacked: struct { IntOne *big.Int }{big.NewInt(1)}, }, { - def: `[{"name":"int_one_","type":"int256"}]`, + def: `[{"components": [{"name":"int_one_","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001", unpacked: struct { IntOne *big.Int }{big.NewInt(1)}, }, { - def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`, + def: `[{"components": [{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002", unpacked: struct { @@ -831,11 +831,11 @@ var packUnpackTests = []packUnpackTest{ }, { // static tuple - def: `[{"name":"a","type":"int64"}, + def: `[{"components": [{"name":"a","type":"int64"}, {"name":"b","type":"int256"}, {"name":"c","type":"int256"}, {"name":"d","type":"bool"}, - {"name":"e","type":"bytes32[3][2]"}]`, + {"name":"e","type":"bytes32[3][2]"}], "type":"tuple"}]`, unpacked: struct { A int64 B *big.Int @@ -855,12 +855,12 @@ var packUnpackTests = []packUnpackTest{ "0500000000000000000000000000000000000000000000000000000000000000", // struct[e] array[1][2] }, { - def: `"type": "tuple","components": [{"name":"a","type":"string"}, + def: `[{"components": [{"name":"a","type":"string"}, {"name":"b","type":"int64"}, {"name":"c","type":"bytes"}, {"name":"d","type":"string[]"}, {"name":"e","type":"int256[]"}, - {"name":"f","type":"address[]"}]`, + {"name":"f","type":"address[]"}], "type":"tuple"}]`, unpacked: struct { FieldA string `abi:"a"` // Test whether abi tag works FieldB int64 `abi:"b"` @@ -894,10 +894,10 @@ var packUnpackTests = []packUnpackTest{ "0000000000000000000000000200000000000000000000000000000000000000", // common.Address{2} }, { - def: `[{"type": "tuple","components": [{"name": "a","type": "uint256"}, + def: `[{"components": [{ "type": "tuple","components": [{"name": "a","type": "uint256"}, {"name": "b","type": "uint256[]"}], "name": "a","type": "tuple"}, - {"name": "b","type": "uint256[]"}]`, + {"name": "b","type": "uint256[]"}], "type": "tuple"}]`, unpacked: struct { A struct { FieldA *big.Int `abi:"a"` From e87a6bee0c3f7dfdbad5b7dd79500b88daf3965b Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 30 Jun 2020 16:15:41 +0200 Subject: [PATCH 07/20] accounts/abi: fixed tests for packing/unpacking --- accounts/abi/argument.go | 31 +++++++++++++++++++++++++++++++ accounts/abi/bind/bind.go | 16 ---------------- accounts/abi/packing_test.go | 2 +- accounts/abi/reflect.go | 15 +++++++++++++++ accounts/abi/unpack_test.go | 4 ++-- go.mod | 2 +- 6 files changed, 50 insertions(+), 20 deletions(-) diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index f4c19ef2e7ca..7b155a26b860 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -84,9 +84,40 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { // Nothing to unmarshal, return default arguments return arguments.makeDefaulArgs(), nil } + if len(arguments) > 1 { + args, err := arguments.UnpackValues(data) + if err != nil { + return args, err + } + return arguments.createStruct(args) + } return arguments.UnpackValues(data) } +func (arguments Arguments) createStruct(values []interface{}) ([]interface{}, error) { + var fields []reflect.StructField + for _, arg := range arguments { + fields = append(fields, reflect.StructField{ + Name: ToCamelCase(arg.Name), // reflect.StructOf will panic for any exported field. + Type: arg.Type.getType(), + Tag: reflect.StructTag("json:\"" + arg.Name + "\""), + }) + } + typ := reflect.New(reflect.StructOf(fields)).Elem() + for i, val := range values { + field := typ.Field(i) + if !field.IsValid() { + return arguments.makeDefaulArgs(), fmt.Errorf("abi: field %v not valid", i) + } + if err := set(field, reflect.ValueOf(val)); err != nil { + return arguments.makeDefaulArgs(), err + } + } + ret := make([]interface{}, 1) + ret[0] = typ.Interface() + return ret, nil +} + func (arguments Arguments) makeDefaulArgs() []interface{} { nonIndexedArgs := arguments.NonIndexed() retval := make([]interface{}, len(nonIndexedArgs)) diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go index fd37a7d9d6c3..0e98709b1455 100644 --- a/accounts/abi/bind/bind.go +++ b/accounts/abi/bind/bind.go @@ -25,7 +25,6 @@ import ( "errors" "fmt" "go/format" - "reflect" "regexp" "strings" "text/template" @@ -585,18 +584,3 @@ func hasStruct(t abi.Type) bool { return false } } - -// ToStruct converts a interface of a runtime type into a interface of the -// given type -// e.g. turn -// var fields []reflect.StructField -// fields = append(fields, reflect.StructField{ -// Name: "X", -// Type: reflect.TypeOf(new(big.Int)), -// Tag: reflect.StructTag("json:\"" + "x" + "\""), -// } -// into -// type TupleT struct { X *big.Int } -func ToStruct(in interface{}, typ interface{}) interface{} { - return reflect.ValueOf(in).Convert(reflect.TypeOf(typ)).Interface() -} diff --git a/accounts/abi/packing_test.go b/accounts/abi/packing_test.go index 8260511690c5..ed9be0b02284 100644 --- a/accounts/abi/packing_test.go +++ b/accounts/abi/packing_test.go @@ -752,7 +752,7 @@ var packUnpackTests = []packUnpackTest{ }{big.NewInt(1)}, }, { - def: `[{"components": [{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}], "type":"tuple"}]`, + def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002", unpacked: struct { diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index f4812b06bf72..2377f6dd7564 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -24,6 +24,21 @@ import ( "strings" ) +// ToStruct converts a interface of a runtime type into a interface of the +// given type +// e.g. turn +// var fields []reflect.StructField +// fields = append(fields, reflect.StructField{ +// Name: "X", +// Type: reflect.TypeOf(new(big.Int)), +// Tag: reflect.StructTag("json:\"" + "x" + "\""), +// } +// into +// type TupleT struct { X *big.Int } +func ToStruct(in interface{}, typ interface{}) interface{} { + return reflect.ValueOf(in).Convert(reflect.TypeOf(typ)).Interface() +} + // indirect recursively dereferences the value until it either gets the value // or finds a big.Int func indirect(v reflect.Value) reflect.Value { diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index 182c222c0400..f0ed8d462261 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -49,8 +49,8 @@ func TestUnpack(t *testing.T) { t.Errorf("test %d (%v) failed: %v", i, test.def, err) return } - if !reflect.DeepEqual(test.unpacked, out[0]) { - t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out) + if !reflect.DeepEqual(test.unpacked, ToStruct(out[0], test.unpacked)) { + t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out[0]) } }) } diff --git a/go.mod b/go.mod index 2a701c3614fa..ae1cf64aaf9c 100755 --- a/go.mod +++ b/go.mod @@ -68,5 +68,5 @@ require ( gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6 gopkg.in/urfave/cli.v1 v1.20.0 - gotest.tools v2.2.0+incompatible // indirect + gotest.tools v2.2.0+incompatible ) From f3e714cb7ceeb3692a7a9773ec7c1add9e05a984 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Wed, 1 Jul 2020 09:12:49 +0200 Subject: [PATCH 08/20] accounts/abi: fixed tests --- accounts/abi/abi_test.go | 6 ++---- accounts/abi/argument.go | 29 +++++++++++++++++++++-------- accounts/abi/bind/template.go | 2 +- accounts/abi/reflect.go | 2 +- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 7fa9d499a21f..689d197c0b56 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -185,10 +185,8 @@ func TestConstructor(t *testing.T) { if err != nil { t.Error(err) } - if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) { - t.Error("Unable to pack/unpack from constructor") - } - if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) { + res := struct{ A, B *big.Int }{big.NewInt(1), big.NewInt(2)} + if !reflect.DeepEqual(ToStruct(unpacked[0], res), res) { t.Error("Unable to pack/unpack from constructor") } } diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index 7b155a26b860..a1ed4963b898 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -83,14 +83,14 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { } // Nothing to unmarshal, return default arguments return arguments.makeDefaulArgs(), nil - } - if len(arguments) > 1 { - args, err := arguments.UnpackValues(data) - if err != nil { - return args, err - } - return arguments.createStruct(args) - } + } /* + if len(arguments) > 1 { + args, err := arguments.UnpackValues(data) + if err != nil { + return args, err + } + return arguments.createStruct(args) + }*/ return arguments.UnpackValues(data) } @@ -118,6 +118,19 @@ func (arguments Arguments) createStruct(values []interface{}) ([]interface{}, er return ret, nil } +/* +func (arguments Argument) createSlice(values []interface{}) ([]interface{}, error) { +case reflect.Slice, reflect.Array: + if value.Len() < len(marshalledValues) { + return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len()) + } + for i := range nonIndexedArgs { + if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil { + return err + } + } +}*/ + func (arguments Arguments) makeDefaulArgs() []interface{} { nonIndexedArgs := arguments.NonIndexed() retval := make([]interface{}, len(nonIndexedArgs)) diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index aeba5731d1a7..91f3020e41d7 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -312,7 +312,7 @@ var ( } {{end}} - return {{if .Structured}}*outstruct,{{else}}{{range $i, $t := .Normalized.Outputs}}bind.ToStruct(out[{{$i}}], *new({{bindtype .Type $structs}})).({{bindtype .Type $structs}}),{{end}}{{end}} err + return {{if .Structured}}*outstruct,{{else}}{{range $i, $t := .Normalized.Outputs}}abi.ToStruct(out[{{$i}}], *new({{bindtype .Type $structs}})).({{bindtype .Type $structs}}),{{end}}{{end}} err } // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}. diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index 2377f6dd7564..9965625f98c0 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -99,7 +99,7 @@ func set(dst, src reflect.Value) error { dst.Set(src) case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice && dst.CanSet(): return setSlice(dst, src) - case dstType.Kind() == reflect.Array: + case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Array: return setArray(dst, src) case dstType.Kind() == reflect.Struct: return setStruct(dst, src) From 7e08e31d00e544278ce0411139cf433451f86bc1 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 2 Jul 2020 12:45:16 +0200 Subject: [PATCH 09/20] accounts/abi: added more logic to ToStruct --- accounts/abi/abi_test.go | 7 +++-- accounts/abi/argument.go | 13 --------- accounts/abi/bind/base_test.go | 7 +++-- accounts/abi/packing_test.go | 28 +++++++++--------- accounts/abi/reflect.go | 52 ++++++++++++++++++++++++++++++++-- 5 files changed, 74 insertions(+), 33 deletions(-) diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 689d197c0b56..ad8acdf52229 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -185,8 +185,11 @@ func TestConstructor(t *testing.T) { if err != nil { t.Error(err) } - res := struct{ A, B *big.Int }{big.NewInt(1), big.NewInt(2)} - if !reflect.DeepEqual(ToStruct(unpacked[0], res), res) { + + if !reflect.DeepEqual(unpacked[0], big.NewInt(1)) { + t.Error("Unable to pack/unpack from constructor") + } + if !reflect.DeepEqual(unpacked[1], big.NewInt(2)) { t.Error("Unable to pack/unpack from constructor") } } diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index a1ed4963b898..b72c56b6dba2 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -118,19 +118,6 @@ func (arguments Arguments) createStruct(values []interface{}) ([]interface{}, er return ret, nil } -/* -func (arguments Argument) createSlice(values []interface{}) ([]interface{}, error) { -case reflect.Slice, reflect.Array: - if value.Len() < len(marshalledValues) { - return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len()) - } - for i := range nonIndexedArgs { - if err := set(value.Index(i), reflect.ValueOf(marshalledValues[i])); err != nil { - return err - } - } -}*/ - func (arguments Arguments) makeDefaulArgs() []interface{} { nonIndexedArgs := arguments.NonIndexed() retval := make([]interface{}, len(nonIndexedArgs)) diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go index c4740f68b750..0b8e3e0d6a41 100644 --- a/accounts/abi/bind/base_test.go +++ b/accounts/abi/bind/base_test.go @@ -74,7 +74,8 @@ func TestPassingBlockNumber(t *testing.T) { blockNumber := big.NewInt(42) - bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something") + var res []interface{} + bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, &res, "something") if mc.callContractBlockNumber != blockNumber { t.Fatalf("CallContract() was not passed the block number") @@ -84,7 +85,7 @@ func TestPassingBlockNumber(t *testing.T) { t.Fatalf("CodeAt() was not passed the block number") } - bc.Call(&bind.CallOpts{}, nil, "something") + bc.Call(&bind.CallOpts{}, &res, "something") if mc.callContractBlockNumber != nil { t.Fatalf("CallContract() was passed a block number when it should not have been") @@ -94,7 +95,7 @@ func TestPassingBlockNumber(t *testing.T) { t.Fatalf("CodeAt() was passed a block number when it should not have been") } - bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something") + bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, &res, "something") if !mc.pendingCallContractCalled { t.Fatalf("CallContract() was not passed the block number") diff --git a/accounts/abi/packing_test.go b/accounts/abi/packing_test.go index ed9be0b02284..eae3b0df2056 100644 --- a/accounts/abi/packing_test.go +++ b/accounts/abi/packing_test.go @@ -752,7 +752,7 @@ var packUnpackTests = []packUnpackTest{ }{big.NewInt(1)}, }, { - def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`, + def: `[{"components": [{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}], "type":"tuple"}]`, packed: "0000000000000000000000000000000000000000000000000000000000000001" + "0000000000000000000000000000000000000000000000000000000000000002", unpacked: struct { @@ -862,14 +862,15 @@ var packUnpackTests = []packUnpackTest{ {"name":"e","type":"int256[]"}, {"name":"f","type":"address[]"}], "type":"tuple"}]`, unpacked: struct { - FieldA string `abi:"a"` // Test whether abi tag works - FieldB int64 `abi:"b"` - C []byte - D []string - E []*big.Int - F []common.Address + A string + B int64 + C []byte + D []string + E []*big.Int + F []common.Address }{"foobar", 1, []byte{1}, []string{"foo", "bar"}, []*big.Int{big.NewInt(1), big.NewInt(-1)}, []common.Address{{1}, {2}}}, - packed: "00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset + packed: "0000000000000000000000000000000000000000000000000000000000000020" + // struct a + "00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset "0000000000000000000000000000000000000000000000000000000000000001" + // struct[b] "0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset "0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset @@ -900,17 +901,18 @@ var packUnpackTests = []packUnpackTest{ {"name": "b","type": "uint256[]"}], "type": "tuple"}]`, unpacked: struct { A struct { - FieldA *big.Int `abi:"a"` - B []*big.Int + A *big.Int + B []*big.Int } B []*big.Int }{ A: struct { - FieldA *big.Int `abi:"a"` // Test whether abi tag works for nested tuple - B []*big.Int + A *big.Int + B []*big.Int }{big.NewInt(1), []*big.Int{big.NewInt(1), big.NewInt(2)}}, B: []*big.Int{big.NewInt(1), big.NewInt(2)}}, - packed: "0000000000000000000000000000000000000000000000000000000000000040" + // a offset + packed: "0000000000000000000000000000000000000000000000000000000000000020" + // struct a + "0000000000000000000000000000000000000000000000000000000000000040" + // a offset "00000000000000000000000000000000000000000000000000000000000000e0" + // b offset "0000000000000000000000000000000000000000000000000000000000000001" + // a.a value "0000000000000000000000000000000000000000000000000000000000000040" + // a.b offset diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index 9965625f98c0..e0e3af83a9c9 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -35,8 +35,54 @@ import ( // } // into // type TupleT struct { X *big.Int } -func ToStruct(in interface{}, typ interface{}) interface{} { - return reflect.ValueOf(in).Convert(reflect.TypeOf(typ)).Interface() +func ToStruct(in interface{}, proto interface{}) interface{} { + inType, protoType := reflect.TypeOf(in), reflect.TypeOf(proto) + switch { + case inType.ConvertibleTo(protoType): + return reflect.ValueOf(in).Convert(protoType).Interface() + case inType.Kind() == reflect.Struct: + if err := copyStruct(proto, in); err != nil { + panic(err) + return nil + } + return proto + case inType.Kind() == reflect.Array || inType.Kind() == reflect.Slice: + if err := copySlice(proto, in); err != nil { + panic(err) + return nil + } + return proto + default: + // Use set as a last ditch effort + if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil { + panic(err) + return nil + } + return proto + } +} + +// copyStruct tries to copy each field of in to out. +func copyStruct(out interface{}, in interface{}) error { + valueIn := reflect.ValueOf(in) + valueOut := reflect.ValueOf(out).Elem() + for i := 0; i < valueOut.NumField(); i++ { + if err := set(valueOut.Field(i), valueIn.Field(i)); err != nil { + return err + } + } + return nil +} + +func copySlice(out interface{}, in interface{}) error { + valueIn := reflect.ValueOf(in) + valueOut := reflect.ValueOf(out).Elem() + for i := 0; i < valueOut.Len(); i++ { + if err := set(valueOut.Index(i), valueIn.Index(i)); err != nil { + return err + } + } + return nil } // indirect recursively dereferences the value until it either gets the value @@ -101,6 +147,8 @@ func set(dst, src reflect.Value) error { return setSlice(dst, src) case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Array: return setArray(dst, src) + case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice: + return setArray(dst, src) case dstType.Kind() == reflect.Struct: return setStruct(dst, src) default: From d2e5302d6eca65b9dbd60316c1c24d90249f3001 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 2 Jul 2020 12:53:07 +0200 Subject: [PATCH 10/20] accounts/abi/bind: fixed template --- accounts/abi/bind/base.go | 3 +++ accounts/abi/bind/base_test.go | 7 +++---- accounts/abi/bind/template.go | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index a24a3fd892cc..576567145742 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -122,6 +122,9 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri if opts == nil { opts = new(CallOpts) } + if results == nil { + results = new([]interface{}) + } // Pack the input, call and unpack the results input, err := c.abi.Pack(method, params...) if err != nil { diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go index 0b8e3e0d6a41..c4740f68b750 100644 --- a/accounts/abi/bind/base_test.go +++ b/accounts/abi/bind/base_test.go @@ -74,8 +74,7 @@ func TestPassingBlockNumber(t *testing.T) { blockNumber := big.NewInt(42) - var res []interface{} - bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, &res, "something") + bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, nil, "something") if mc.callContractBlockNumber != blockNumber { t.Fatalf("CallContract() was not passed the block number") @@ -85,7 +84,7 @@ func TestPassingBlockNumber(t *testing.T) { t.Fatalf("CodeAt() was not passed the block number") } - bc.Call(&bind.CallOpts{}, &res, "something") + bc.Call(&bind.CallOpts{}, nil, "something") if mc.callContractBlockNumber != nil { t.Fatalf("CallContract() was passed a block number when it should not have been") @@ -95,7 +94,7 @@ func TestPassingBlockNumber(t *testing.T) { t.Fatalf("CodeAt() was passed a block number when it should not have been") } - bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, &res, "something") + bc.Call(&bind.CallOpts{BlockNumber: blockNumber, Pending: true}, nil, "something") if !mc.pendingCallContractCalled { t.Fatalf("CallContract() was not passed the block number") diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index 91f3020e41d7..4d3c5b84dd8f 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -312,7 +312,7 @@ var ( } {{end}} - return {{if .Structured}}*outstruct,{{else}}{{range $i, $t := .Normalized.Outputs}}abi.ToStruct(out[{{$i}}], *new({{bindtype .Type $structs}})).({{bindtype .Type $structs}}),{{end}}{{end}} err + return {{if .Structured}}*outstruct,{{else}}{{range $i, $t := .Normalized.Outputs}}*abi.ToStruct(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}),{{end}}{{end}} err } // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}. From e442a50b79381c3f36272d2617e7c27af24e1008 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 2 Jul 2020 13:14:39 +0200 Subject: [PATCH 11/20] accounts/abi/bind: fixed ToStruct conversion --- accounts/abi/reflect.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index e0e3af83a9c9..c590e84f53e5 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -42,20 +42,17 @@ func ToStruct(in interface{}, proto interface{}) interface{} { return reflect.ValueOf(in).Convert(protoType).Interface() case inType.Kind() == reflect.Struct: if err := copyStruct(proto, in); err != nil { - panic(err) return nil } return proto - case inType.Kind() == reflect.Array || inType.Kind() == reflect.Slice: - if err := copySlice(proto, in); err != nil { - panic(err) + case inType.Kind() == reflect.Array: + if err := copyArray(proto, in); err != nil { return nil } return proto default: // Use set as a last ditch effort if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil { - panic(err) return nil } return proto @@ -74,7 +71,7 @@ func copyStruct(out interface{}, in interface{}) error { return nil } -func copySlice(out interface{}, in interface{}) error { +func copyArray(out interface{}, in interface{}) error { valueIn := reflect.ValueOf(in) valueOut := reflect.ValueOf(out).Elem() for i := 0; i < valueOut.Len(); i++ { From 36439b951b6924e7fa118e5d656f282faa371260 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 6 Jul 2020 09:28:40 +0200 Subject: [PATCH 12/20] accounts/abi/: removed unused code --- accounts/abi/argument.go | 33 +-------------------------------- accounts/abi/bind/base.go | 2 +- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index b72c56b6dba2..f4c19ef2e7ca 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -83,39 +83,8 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { } // Nothing to unmarshal, return default arguments return arguments.makeDefaulArgs(), nil - } /* - if len(arguments) > 1 { - args, err := arguments.UnpackValues(data) - if err != nil { - return args, err - } - return arguments.createStruct(args) - }*/ - return arguments.UnpackValues(data) -} - -func (arguments Arguments) createStruct(values []interface{}) ([]interface{}, error) { - var fields []reflect.StructField - for _, arg := range arguments { - fields = append(fields, reflect.StructField{ - Name: ToCamelCase(arg.Name), // reflect.StructOf will panic for any exported field. - Type: arg.Type.getType(), - Tag: reflect.StructTag("json:\"" + arg.Name + "\""), - }) } - typ := reflect.New(reflect.StructOf(fields)).Elem() - for i, val := range values { - field := typ.Field(i) - if !field.IsValid() { - return arguments.makeDefaulArgs(), fmt.Errorf("abi: field %v not valid", i) - } - if err := set(field, reflect.ValueOf(val)); err != nil { - return arguments.makeDefaulArgs(), err - } - } - ret := make([]interface{}, 1) - ret[0] = typ.Interface() - return ret, nil + return arguments.UnpackValues(data) } func (arguments Arguments) makeDefaulArgs() []interface{} { diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go index 576567145742..0216f6b544ae 100644 --- a/accounts/abi/bind/base.go +++ b/accounts/abi/bind/base.go @@ -162,7 +162,7 @@ func (c *BoundContract) Call(opts *CallOpts, results *[]interface{}, method stri } } - if results == nil || len(*results) == 0 { + if len(*results) == 0 { res, err := c.abi.Unpack(method, output) *results = res return err From 5bbdaba7cc865a6196bccf604403fcd7229a1d63 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 13 Jul 2020 15:37:01 +0200 Subject: [PATCH 13/20] accounts/abi: updated template --- accounts/abi/bind/template.go | 14 +++++++++----- accounts/abi/reflect.go | 10 +++++----- accounts/abi/unpack_test.go | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/accounts/abi/bind/template.go b/accounts/abi/bind/template.go index 4d3c5b84dd8f..5329b3ebc322 100644 --- a/accounts/abi/bind/template.go +++ b/accounts/abi/bind/template.go @@ -303,16 +303,20 @@ var ( var out []interface{} err := _{{$contract.Type}}.contract.Call(opts, &out, "{{.Original.Name}}" {{range .Normalized.Inputs}}, {{.Name}}{{end}}) {{if .Structured}} - outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} }) - {{range $i, $t := .Normalized.Outputs}} - outstruct.{{.Name}} = out[{{$i}}].({{bindtype .Type $structs}}){{end}} + outstruct := new(struct{ {{range .Normalized.Outputs}} {{.Name}} {{bindtype .Type $structs}}; {{end}} }) + {{range $i, $t := .Normalized.Outputs}} + outstruct.{{.Name}} = out[{{$i}}].({{bindtype .Type $structs}}){{end}} + + return *outstruct, err {{else}} if err != nil { return {{range $i, $_ := .Normalized.Outputs}}*new({{bindtype .Type $structs}}), {{end}} err } - {{end}} + {{range $i, $t := .Normalized.Outputs}} + out{{$i}} := *abi.ConvertType(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}){{end}} - return {{if .Structured}}*outstruct,{{else}}{{range $i, $t := .Normalized.Outputs}}*abi.ToStruct(out[{{$i}}], new({{bindtype .Type $structs}})).(*{{bindtype .Type $structs}}),{{end}}{{end}} err + return {{range $i, $t := .Normalized.Outputs}}out{{$i}}, {{end}} err + {{end}} } // {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}. diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index c590e84f53e5..96292a31450a 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -24,7 +24,7 @@ import ( "strings" ) -// ToStruct converts a interface of a runtime type into a interface of the +// ConvertType converts a interface of a runtime type into a interface of the // given type // e.g. turn // var fields []reflect.StructField @@ -35,25 +35,25 @@ import ( // } // into // type TupleT struct { X *big.Int } -func ToStruct(in interface{}, proto interface{}) interface{} { +func ConvertType(in interface{}, proto interface{}) interface{} { inType, protoType := reflect.TypeOf(in), reflect.TypeOf(proto) switch { case inType.ConvertibleTo(protoType): return reflect.ValueOf(in).Convert(protoType).Interface() case inType.Kind() == reflect.Struct: if err := copyStruct(proto, in); err != nil { - return nil + panic(err) } return proto case inType.Kind() == reflect.Array: if err := copyArray(proto, in); err != nil { - return nil + panic(err) } return proto default: // Use set as a last ditch effort if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil { - return nil + panic(err) } return proto } diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index f0ed8d462261..b88f77805bfa 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -49,7 +49,7 @@ func TestUnpack(t *testing.T) { t.Errorf("test %d (%v) failed: %v", i, test.def, err) return } - if !reflect.DeepEqual(test.unpacked, ToStruct(out[0], test.unpacked)) { + if !reflect.DeepEqual(test.unpacked, ConvertType(out[0], test.unpacked)) { t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.unpacked, out[0]) } }) From 9dd8e6d1081de08544f102551a744b9c301c34a9 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 13 Jul 2020 15:48:02 +0200 Subject: [PATCH 14/20] accounts/abi: refactored unused code --- accounts/abi/argument.go | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index f4c19ef2e7ca..50cf7ae819b7 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -79,14 +79,20 @@ func (arguments Arguments) isTuple() bool { func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { if len(data) == 0 { if len(arguments) != 0 { - return arguments.makeDefaulArgs(), fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") + return nil, fmt.Errorf("abi: attempting to unmarshall an empty string while arguments are expected") } - // Nothing to unmarshal, return default arguments - return arguments.makeDefaulArgs(), nil + // Nothing to unmarshal, return default variables + nonIndexedArgs := arguments.NonIndexed() + defaultVars := make([]interface{}, len(nonIndexedArgs)) + for index, arg := range nonIndexedArgs { + defaultVars[index] = reflect.New(arg.Type.getType()) + } + return defaultVars, nil } return arguments.UnpackValues(data) } +<<<<<<< HEAD func (arguments Arguments) makeDefaulArgs() []interface{} { nonIndexedArgs := arguments.NonIndexed() retval := make([]interface{}, len(nonIndexedArgs)) @@ -97,6 +103,9 @@ func (arguments Arguments) makeDefaulArgs() []interface{} { } // UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value. +======= +// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value +>>>>>>> 498675bc1... accounts/abi: refactored unused code func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error { // Make sure map is not nil if v == nil { @@ -214,7 +223,7 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) { virtualArgs += getTypeSize(arg.Type)/32 - 1 } if err != nil { - return arguments.makeDefaulArgs(), err + return nil, err } retval = append(retval, marshalledValue) } From e95dfd7aa9dd0cb5fe97c6015e638e89bc4c05d5 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 13 Jul 2020 16:02:36 +0200 Subject: [PATCH 15/20] contracts/checkpointoracle: updated contracts to sol ^0.6.0 --- contracts/checkpointoracle/contract/oracle.go | 47 ++++++++++++++----- .../checkpointoracle/contract/oracle.sol | 2 +- 2 files changed, 36 insertions(+), 13 deletions(-) diff --git a/contracts/checkpointoracle/contract/oracle.go b/contracts/checkpointoracle/contract/oracle.go index fc6795bab27d..a4a308f5c524 100644 --- a/contracts/checkpointoracle/contract/oracle.go +++ b/contracts/checkpointoracle/contract/oracle.go @@ -27,10 +27,17 @@ var ( ) // CheckpointOracleABI is the input ABI used to generate the binding from. -const CheckpointOracleABI = "[{\"constant\":true,\"inputs\":[],\"name\":\"GetAllAdmin\",\"outputs\":[{\"name\":\"\",\"type\":\"address[]\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"GetLatestCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"uint64\"},{\"name\":\"\",\"type\":\"bytes32\"},{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_recentNumber\",\"type\":\"uint256\"},{\"name\":\"_recentHash\",\"type\":\"bytes32\"},{\"name\":\"_hash\",\"type\":\"bytes32\"},{\"name\":\"_sectionIndex\",\"type\":\"uint64\"},{\"name\":\"v\",\"type\":\"uint8[]\"},{\"name\":\"r\",\"type\":\"bytes32[]\"},{\"name\":\"s\",\"type\":\"bytes32[]\"}],\"name\":\"SetCheckpoint\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"name\":\"_adminlist\",\"type\":\"address[]\"},{\"name\":\"_sectionSize\",\"type\":\"uint256\"},{\"name\":\"_processConfirms\",\"type\":\"uint256\"},{\"name\":\"_threshold\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"index\",\"type\":\"uint64\"},{\"indexed\":false,\"name\":\"checkpointHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"v\",\"type\":\"uint8\"},{\"indexed\":false,\"name\":\"r\",\"type\":\"bytes32\"},{\"indexed\":false,\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"NewCheckpointVote\",\"type\":\"event\"}]" +const CheckpointOracleABI = "[{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"_adminlist\",\"type\":\"address[]\"},{\"internalType\":\"uint256\",\"name\":\"_sectionSize\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_processConfirms\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_threshold\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint64\",\"name\":\"index\",\"type\":\"uint64\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"checkpointHash\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"v\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"r\",\"type\":\"bytes32\"},{\"indexed\":false,\"internalType\":\"bytes32\",\"name\":\"s\",\"type\":\"bytes32\"}],\"name\":\"NewCheckpointVote\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"GetAllAdmin\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"GetLatestCheckpoint\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"},{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_recentNumber\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_recentHash\",\"type\":\"bytes32\"},{\"internalType\":\"bytes32\",\"name\":\"_hash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"_sectionIndex\",\"type\":\"uint64\"},{\"internalType\":\"uint8[]\",\"name\":\"v\",\"type\":\"uint8[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"r\",\"type\":\"bytes32[]\"},{\"internalType\":\"bytes32[]\",\"name\":\"s\",\"type\":\"bytes32[]\"}],\"name\":\"SetCheckpoint\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]" + +// CheckpointOracleFuncSigs maps the 4-byte function signature to its string representation. +var CheckpointOracleFuncSigs = map[string]string{ + "45848dfc": "GetAllAdmin()", + "4d6a304c": "GetLatestCheckpoint()", + "d459fc46": "SetCheckpoint(uint256,bytes32,bytes32,uint64,uint8[],bytes32[],bytes32[])", +} // CheckpointOracleBin is the compiled bytecode used for deploying new contracts. -const CheckpointOracleBin = `0x608060405234801561001057600080fd5b506040516108153803806108158339818101604052608081101561003357600080fd5b81019080805164010000000081111561004b57600080fd5b8201602081018481111561005e57600080fd5b815185602082028301116401000000008211171561007b57600080fd5b505060208201516040830151606090930151919450925060005b84518110156101415760016000808784815181106100af57fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff02191690831515021790555060018582815181106100fc57fe5b60209081029190910181015182546001808201855560009485529290932090920180546001600160a01b0319166001600160a01b039093169290921790915501610095565b50600592909255600655600755506106b78061015e6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806345848dfc146100465780634d6a304c1461009e578063d459fc46146100cf575b600080fd5b61004e6102b0565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561008a578181015183820152602001610072565b505050509050019250505060405180910390f35b6100a661034f565b6040805167ffffffffffffffff9094168452602084019290925282820152519081900360600190f35b61029c600480360360e08110156100e557600080fd5b81359160208101359160408201359167ffffffffffffffff6060820135169181019060a08101608082013564010000000081111561012257600080fd5b82018360208201111561013457600080fd5b8035906020019184602083028401116401000000008311171561015657600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156101a657600080fd5b8201836020820111156101b857600080fd5b803590602001918460208302840111640100000000831117156101da57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561022a57600080fd5b82018360208201111561023c57600080fd5b8035906020019184602083028401116401000000008311171561025e57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092955061036a945050505050565b604080519115158252519081900360200190f35b6060806001805490506040519080825280602002602001820160405280156102e2578160200160208202803883390190505b50905060005b60015481101561034957600181815481106102ff57fe5b9060005260206000200160009054906101000a90046001600160a01b031682828151811061032957fe5b6001600160a01b03909216602092830291909101909101526001016102e8565b50905090565b60025460045460035467ffffffffffffffff90921691909192565b3360009081526020819052604081205460ff1661038657600080fd5b8688401461039357600080fd5b82518451146103a157600080fd5b81518451146103af57600080fd5b6006546005548660010167ffffffffffffffff1602014310156103d457506000610677565b60025467ffffffffffffffff90811690861610156103f457506000610677565b60025467ffffffffffffffff8681169116148015610426575067ffffffffffffffff8516151580610426575060035415155b1561043357506000610677565b8561044057506000610677565b60408051601960f81b6020808301919091526000602183018190523060601b60228401526001600160c01b031960c08a901b166036840152603e8084018b905284518085039091018152605e909301909352815191012090805b86518110156106715760006001848984815181106104b457fe5b60200260200101518985815181106104c857fe5b60200260200101518986815181106104dc57fe5b602002602001015160405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa15801561053b573d6000803e3d6000fd5b505060408051601f1901516001600160a01b03811660009081526020819052919091205490925060ff16905061057057600080fd5b826001600160a01b0316816001600160a01b03161161058e57600080fd5b8092508867ffffffffffffffff167fce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a418b8a85815181106105ca57fe5b60200260200101518a86815181106105de57fe5b60200260200101518a87815181106105f257fe5b6020026020010151604051808581526020018460ff1660ff16815260200183815260200182815260200194505050505060405180910390a260075482600101106106685750505060048790555050436003556002805467ffffffffffffffff191667ffffffffffffffff86161790556001610677565b5060010161049a565b50600080fd5b97965050505050505056fea265627a7a723058207f6a191ce575596a2f1e907c8c0a01003d16b69fb2c4f432d10878e8c0a99a0264736f6c634300050a0032` +var CheckpointOracleBin = "0x608060405234801561001057600080fd5b506040516108703803806108708339818101604052608081101561003357600080fd5b810190808051604051939291908464010000000082111561005357600080fd5b90830190602082018581111561006857600080fd5b825186602082028301116401000000008211171561008557600080fd5b82525081516020918201928201910280838360005b838110156100b257818101518382015260200161009a565b50505050919091016040908152602083015190830151606090930151909450919250600090505b84518110156101855760016000808784815181106100f357fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060006101000a81548160ff021916908315150217905550600185828151811061014057fe5b60209081029190910181015182546001808201855560009485529290932090920180546001600160a01b0319166001600160a01b0390931692909217909155016100d9565b50600592909255600655600755506106ce806101a26000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806345848dfc146100465780634d6a304c1461009e578063d459fc46146100cf575b600080fd5b61004e6102b0565b60408051602080825283518183015283519192839290830191858101910280838360005b8381101561008a578181015183820152602001610072565b505050509050019250505060405180910390f35b6100a6610365565b6040805167ffffffffffffffff9094168452602084019290925282820152519081900360600190f35b61029c600480360360e08110156100e557600080fd5b81359160208101359160408201359167ffffffffffffffff6060820135169181019060a08101608082013564010000000081111561012257600080fd5b82018360208201111561013457600080fd5b8035906020019184602083028401116401000000008311171561015657600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525092959493602081019350359150506401000000008111156101a657600080fd5b8201836020820111156101b857600080fd5b803590602001918460208302840111640100000000831117156101da57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929594936020810193503591505064010000000081111561022a57600080fd5b82018360208201111561023c57600080fd5b8035906020019184602083028401116401000000008311171561025e57600080fd5b919080806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250929550610380945050505050565b604080519115158252519081900360200190f35b600154606090819067ffffffffffffffff811180156102ce57600080fd5b506040519080825280602002602001820160405280156102f8578160200160208202803683370190505b50905060005b60015481101561035f576001818154811061031557fe5b9060005260206000200160009054906101000a90046001600160a01b031682828151811061033f57fe5b6001600160a01b03909216602092830291909101909101526001016102fe565b50905090565b60025460045460035467ffffffffffffffff90921691909192565b3360009081526020819052604081205460ff1661039c57600080fd5b868840146103a957600080fd5b82518451146103b757600080fd5b81518451146103c557600080fd5b6006546005548660010167ffffffffffffffff1602014310156103ea5750600061068d565b60025467ffffffffffffffff908116908616101561040a5750600061068d565b60025467ffffffffffffffff868116911614801561043c575067ffffffffffffffff851615158061043c575060035415155b156104495750600061068d565b856104565750600061068d565b60408051601960f81b6020808301919091526000602183018190523060601b60228401526001600160c01b031960c08a901b166036840152603e8084018b905284518085039091018152605e909301909352815191012090805b86518110156106875760006001848984815181106104ca57fe5b60200260200101518985815181106104de57fe5b60200260200101518986815181106104f257fe5b602002602001015160405160008152602001604052604051808581526020018460ff1660ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa158015610551573d6000803e3d6000fd5b505060408051601f1901516001600160a01b03811660009081526020819052919091205490925060ff16905061058657600080fd5b826001600160a01b0316816001600160a01b0316116105a457600080fd5b8092508867ffffffffffffffff167fce51ffa16246bcaf0899f6504f473cd0114f430f566cef71ab7e03d3dde42a418b8a85815181106105e057fe5b60200260200101518a86815181106105f457fe5b60200260200101518a878151811061060857fe5b6020026020010151604051808581526020018460ff1660ff16815260200183815260200182815260200194505050505060405180910390a2600754826001011061067e5750505060048790555050436003556002805467ffffffffffffffff191667ffffffffffffffff8616179055600161068d565b506001016104b0565b50600080fd5b97965050505050505056fea26469706673582212202ddf9eda76bf59c0fc65584c0b22d84ecef2c703765de60439596d6ac34c2b7264736f6c634300060b0033" // DeployCheckpointOracle deploys a new Ethereum contract, binding an instance of CheckpointOracle to it. func DeployCheckpointOracle(auth *bind.TransactOpts, backend bind.ContractBackend, _adminlist []common.Address, _sectionSize *big.Int, _processConfirms *big.Int, _threshold *big.Int) (common.Address, *types.Transaction, *CheckpointOracle, error) { @@ -38,6 +45,7 @@ func DeployCheckpointOracle(auth *bind.TransactOpts, backend bind.ContractBacken if err != nil { return common.Address{}, nil, nil, err } + address, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(CheckpointOracleBin), backend, _adminlist, _sectionSize, _processConfirms, _threshold) if err != nil { return common.Address{}, nil, nil, err @@ -189,49 +197,64 @@ func (_CheckpointOracle *CheckpointOracleTransactorRaw) Transact(opts *bind.Tran // GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc. // -// Solidity: function GetAllAdmin() constant returns(address[]) +// Solidity: function GetAllAdmin() view returns(address[]) func (_CheckpointOracle *CheckpointOracleCaller) GetAllAdmin(opts *bind.CallOpts) ([]common.Address, error) { var out []interface{} err := _CheckpointOracle.contract.Call(opts, &out, "GetAllAdmin") - return out[0].([]common.Address), err + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + } // GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc. // -// Solidity: function GetAllAdmin() constant returns(address[]) +// Solidity: function GetAllAdmin() view returns(address[]) func (_CheckpointOracle *CheckpointOracleSession) GetAllAdmin() ([]common.Address, error) { return _CheckpointOracle.Contract.GetAllAdmin(&_CheckpointOracle.CallOpts) } // GetAllAdmin is a free data retrieval call binding the contract method 0x45848dfc. // -// Solidity: function GetAllAdmin() constant returns(address[]) +// Solidity: function GetAllAdmin() view returns(address[]) func (_CheckpointOracle *CheckpointOracleCallerSession) GetAllAdmin() ([]common.Address, error) { return _CheckpointOracle.Contract.GetAllAdmin(&_CheckpointOracle.CallOpts) } // GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c. // -// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256) +// Solidity: function GetLatestCheckpoint() view returns(uint64, bytes32, uint256) func (_CheckpointOracle *CheckpointOracleCaller) GetLatestCheckpoint(opts *bind.CallOpts) (uint64, [32]byte, *big.Int, error) { var out []interface{} err := _CheckpointOracle.contract.Call(opts, &out, "GetLatestCheckpoint") - if err != nil || len(out) != 3 { - return uint64(0), [32]byte{}, nil, err + + if err != nil { + return *new(uint64), *new([32]byte), *new(*big.Int), err } - return out[0].(uint64), out[1].([32]byte), out[2].(*big.Int), err + + out0 := *abi.ConvertType(out[0], new(uint64)).(*uint64) + out1 := *abi.ConvertType(out[1], new([32]byte)).(*[32]byte) + out2 := *abi.ConvertType(out[2], new(*big.Int)).(**big.Int) + + return out0, out1, out2, err + } // GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c. // -// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256) +// Solidity: function GetLatestCheckpoint() view returns(uint64, bytes32, uint256) func (_CheckpointOracle *CheckpointOracleSession) GetLatestCheckpoint() (uint64, [32]byte, *big.Int, error) { return _CheckpointOracle.Contract.GetLatestCheckpoint(&_CheckpointOracle.CallOpts) } // GetLatestCheckpoint is a free data retrieval call binding the contract method 0x4d6a304c. // -// Solidity: function GetLatestCheckpoint() constant returns(uint64, bytes32, uint256) +// Solidity: function GetLatestCheckpoint() view returns(uint64, bytes32, uint256) func (_CheckpointOracle *CheckpointOracleCallerSession) GetLatestCheckpoint() (uint64, [32]byte, *big.Int, error) { return _CheckpointOracle.Contract.GetLatestCheckpoint(&_CheckpointOracle.CallOpts) } diff --git a/contracts/checkpointoracle/contract/oracle.sol b/contracts/checkpointoracle/contract/oracle.sol index 010644727374..65bac09d28bf 100644 --- a/contracts/checkpointoracle/contract/oracle.sol +++ b/contracts/checkpointoracle/contract/oracle.sol @@ -1,4 +1,4 @@ -pragma solidity ^0.5.10; +pragma solidity ^0.6.0; /** * @title CheckpointOracle From 644bc14d55eae521b50087fc2fe4f4c9c7d13845 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Mon, 13 Jul 2020 17:20:01 +0200 Subject: [PATCH 16/20] accounts/abi: refactored reflection logic --- accounts/abi/reflect.go | 54 ++++++---------------------- accounts/abi/reflect_test.go | 70 ++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 44 deletions(-) diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index 96292a31450a..30fe8e96e380 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -36,50 +36,15 @@ import ( // into // type TupleT struct { X *big.Int } func ConvertType(in interface{}, proto interface{}) interface{} { - inType, protoType := reflect.TypeOf(in), reflect.TypeOf(proto) - switch { - case inType.ConvertibleTo(protoType): + protoType := reflect.TypeOf(proto) + if reflect.TypeOf(in).ConvertibleTo(protoType) { return reflect.ValueOf(in).Convert(protoType).Interface() - case inType.Kind() == reflect.Struct: - if err := copyStruct(proto, in); err != nil { - panic(err) - } - return proto - case inType.Kind() == reflect.Array: - if err := copyArray(proto, in); err != nil { - panic(err) - } - return proto - default: - // Use set as a last ditch effort - if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil { - panic(err) - } - return proto - } -} - -// copyStruct tries to copy each field of in to out. -func copyStruct(out interface{}, in interface{}) error { - valueIn := reflect.ValueOf(in) - valueOut := reflect.ValueOf(out).Elem() - for i := 0; i < valueOut.NumField(); i++ { - if err := set(valueOut.Field(i), valueIn.Field(i)); err != nil { - return err - } } - return nil -} - -func copyArray(out interface{}, in interface{}) error { - valueIn := reflect.ValueOf(in) - valueOut := reflect.ValueOf(out).Elem() - for i := 0; i < valueOut.Len(); i++ { - if err := set(valueOut.Index(i), valueIn.Index(i)); err != nil { - return err - } + // Use set as a last ditch effort + if err := set(reflect.ValueOf(proto), reflect.ValueOf(in)); err != nil { + panic(err) } - return nil + return proto } // indirect recursively dereferences the value until it either gets the value @@ -142,9 +107,7 @@ func set(dst, src reflect.Value) error { dst.Set(src) case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice && dst.CanSet(): return setSlice(dst, src) - case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Array: - return setArray(dst, src) - case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice: + case dstType.Kind() == reflect.Array: return setArray(dst, src) case dstType.Kind() == reflect.Struct: return setStruct(dst, src) @@ -179,6 +142,9 @@ func setSlice(dst, src reflect.Value) error { } func setArray(dst, src reflect.Value) error { + if src.Kind() == reflect.Ptr { + return set(dst, indirect(src)) + } array := reflect.New(dst.Type()).Elem() min := src.Len() if src.Len() > dst.Len() { diff --git a/accounts/abi/reflect_test.go b/accounts/abi/reflect_test.go index c425e6e54bff..bac4cd953010 100644 --- a/accounts/abi/reflect_test.go +++ b/accounts/abi/reflect_test.go @@ -17,6 +17,7 @@ package abi import ( + "math/big" "reflect" "testing" ) @@ -189,3 +190,72 @@ func TestReflectNameToStruct(t *testing.T) { }) } } + +func TestConvertType(t *testing.T) { + // Test Basic Struct + type T struct { + X *big.Int + Y *big.Int + } + // Create on-the-fly structure + var fields []reflect.StructField + fields = append(fields, reflect.StructField{ + Name: "X", + Type: reflect.TypeOf(new(big.Int)), + Tag: reflect.StructTag("json:\"" + "x" + "\""), + }) + fields = append(fields, reflect.StructField{ + Name: "Y", + Type: reflect.TypeOf(new(big.Int)), + Tag: reflect.StructTag("json:\"" + "y" + "\""), + }) + val := reflect.New(reflect.StructOf(fields)) + val.Elem().Field(0).Set(reflect.ValueOf(big.NewInt(1))) + val.Elem().Field(1).Set(reflect.ValueOf(big.NewInt(2))) + // ConvertType + out := *ConvertType(val.Interface(), new(T)).(*T) + if out.X.Cmp(big.NewInt(1)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out.X, big.NewInt(1)) + } + if out.Y.Cmp(big.NewInt(2)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out.Y, big.NewInt(2)) + } + // Slice Type + val2 := reflect.MakeSlice(reflect.SliceOf(reflect.StructOf(fields)), 2, 2) + val2.Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1))) + val2.Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2))) + val2.Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3))) + val2.Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4))) + out2 := *ConvertType(val2.Interface(), new([]T)).(*[]T) + if out2[0].X.Cmp(big.NewInt(1)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1)) + } + if out2[0].Y.Cmp(big.NewInt(2)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2)) + } + if out2[1].X.Cmp(big.NewInt(3)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out2[0].X, big.NewInt(1)) + } + if out2[1].Y.Cmp(big.NewInt(4)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out2[1].Y, big.NewInt(2)) + } + // Array Type + val3 := reflect.New(reflect.ArrayOf(2, reflect.StructOf(fields))) + val3.Elem().Index(0).Field(0).Set(reflect.ValueOf(big.NewInt(1))) + val3.Elem().Index(0).Field(1).Set(reflect.ValueOf(big.NewInt(2))) + val3.Elem().Index(1).Field(0).Set(reflect.ValueOf(big.NewInt(3))) + val3.Elem().Index(1).Field(1).Set(reflect.ValueOf(big.NewInt(4))) + out3 := *ConvertType(val3.Interface(), new([2]T)).(*[2]T) + if out3[0].X.Cmp(big.NewInt(1)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1)) + } + if out3[0].Y.Cmp(big.NewInt(2)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2)) + } + if out3[1].X.Cmp(big.NewInt(3)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out3[0].X, big.NewInt(1)) + } + if out3[1].Y.Cmp(big.NewInt(4)) != 0 { + t.Errorf("ConvertType failed, got %v want %v", out3[1].Y, big.NewInt(2)) + } +} From f74d73cce37286b6e448b98ee9c1ef6070cd5ac4 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Tue, 14 Jul 2020 09:52:22 +0200 Subject: [PATCH 17/20] accounts/abi: less code duplication in Unpack* --- accounts/abi/abi.go | 44 +++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 5a07f2fb972a..5568a911784a 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -80,8 +80,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { return append(method.ID, arguments...), nil } -// Unpack unpacks the output according to the abi specification. -func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) { +func (abi ABI) getArguments(name string, data []byte) (Arguments, error) { // since there can't be naming collisions with contracts and events, // we need to decide whether we're calling a method or an event var args Arguments @@ -97,6 +96,15 @@ func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) { if args == nil { return nil, errors.New("abi: could not locate named method or event") } + return args, nil +} + +// Unpack unpacks the output according to the abi specification. +func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) { + args, err := abi.getArguments(name, data) + if err != nil { + return nil, err + } return args.Unpack(data) } @@ -104,20 +112,9 @@ func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) { // It performs an additional copy. Please only use, if you want to unpack into a // structure that does not strictly confirm to the abi structure (e.g. has additional arguments) func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error { - // since there can't be naming collisions with contracts and events, - // we need to decide whether we're calling a method or an event - var args Arguments - if method, ok := abi.Methods[name]; ok { - if len(data)%32 != 0 { - return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(data), data) - } - args = method.Outputs - } - if event, ok := abi.Events[name]; ok { - args = event.Inputs - } - if args == nil { - return errors.New("abi: could not locate named method or event") + args, err := abi.getArguments(name, data) + if err != nil { + return err } unpacked, err := args.Unpack(data) if err != nil { @@ -128,18 +125,11 @@ func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) erro // UnpackIntoMap unpacks a log into the provided map[string]interface{}. func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte) (err error) { - // since there can't be naming collisions with contracts and events, - // we need to decide whether we're calling a method or an event - if method, ok := abi.Methods[name]; ok { - if len(data)%32 != 0 { - return fmt.Errorf("abi: improperly formatted output") - } - return method.Outputs.UnpackIntoMap(v, data) - } - if event, ok := abi.Events[name]; ok { - return event.Inputs.UnpackIntoMap(v, data) + args, err := abi.getArguments(name, data) + if err != nil { + return err } - return fmt.Errorf("abi: could not locate named method or event") + return args.UnpackIntoMap(v, data) } // UnmarshalJSON implements json.Unmarshaler interface. From 368a952836298fa7825991161781358b61820ad1 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Fri, 7 Aug 2020 09:11:16 +0200 Subject: [PATCH 18/20] accounts/abi: fixed rebasing bug --- accounts/abi/argument.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index 50cf7ae819b7..8a8b1e0ff741 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -85,7 +85,7 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { nonIndexedArgs := arguments.NonIndexed() defaultVars := make([]interface{}, len(nonIndexedArgs)) for index, arg := range nonIndexedArgs { - defaultVars[index] = reflect.New(arg.Type.getType()) + defaultVars[index] = reflect.New(arg.Type.GetType()) } return defaultVars, nil } From 56328d7029f04287a8e6ad41bc14e994c3eebdc1 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet Date: Tue, 15 Sep 2020 14:19:47 +0200 Subject: [PATCH 19/20] fix a few typos in comments --- accounts/abi/abi.go | 2 +- accounts/abi/reflect.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 5568a911784a..5ca7c241db95 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -110,7 +110,7 @@ func (abi ABI) Unpack(name string, data []byte) ([]interface{}, error) { // UnpackIntoInterface unpacks the output in v according to the abi specification. // It performs an additional copy. Please only use, if you want to unpack into a -// structure that does not strictly confirm to the abi structure (e.g. has additional arguments) +// structure that does not strictly conform to the abi structure (e.g. has additional arguments) func (abi ABI) UnpackIntoInterface(v interface{}, name string, data []byte) error { args, err := abi.getArguments(name, data) if err != nil { diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index 30fe8e96e380..11248e07302e 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -24,7 +24,7 @@ import ( "strings" ) -// ConvertType converts a interface of a runtime type into a interface of the +// ConvertType converts an interface of a runtime type into a interface of the // given type // e.g. turn // var fields []reflect.StructField From 6d1d6804c66e575f7b961241c7766f526fb978b1 Mon Sep 17 00:00:00 2001 From: Marius van der Wijden Date: Thu, 24 Sep 2020 12:00:39 +0200 Subject: [PATCH 20/20] rebase on master --- accounts/abi/argument.go | 13 ------------- tests/fuzzers/abi/abifuzzer.go | 4 ++-- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go index 8a8b1e0ff741..e6d52455965d 100644 --- a/accounts/abi/argument.go +++ b/accounts/abi/argument.go @@ -92,20 +92,7 @@ func (arguments Arguments) Unpack(data []byte) ([]interface{}, error) { return arguments.UnpackValues(data) } -<<<<<<< HEAD -func (arguments Arguments) makeDefaulArgs() []interface{} { - nonIndexedArgs := arguments.NonIndexed() - retval := make([]interface{}, len(nonIndexedArgs)) - for index, arg := range nonIndexedArgs { - retval[index] = reflect.New(arg.Type.getType()) - } - return retval -} - // UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value. -======= -// UnpackIntoMap performs the operation hexdata -> mapping of argument name to argument value ->>>>>>> 498675bc1... accounts/abi: refactored unused code func (arguments Arguments) UnpackIntoMap(v map[string]interface{}, data []byte) error { // Make sure map is not nil if v == nil { diff --git a/tests/fuzzers/abi/abifuzzer.go b/tests/fuzzers/abi/abifuzzer.go index ed5c7c05868b..76d3c800f742 100644 --- a/tests/fuzzers/abi/abifuzzer.go +++ b/tests/fuzzers/abi/abifuzzer.go @@ -30,7 +30,7 @@ import ( func unpackPack(abi abi.ABI, method string, inputType []interface{}, input []byte) bool { outptr := reflect.New(reflect.TypeOf(inputType)) - if err := abi.Unpack(outptr.Interface(), method, input); err == nil { + if err := abi.UnpackIntoInterface(outptr.Interface(), method, input); err == nil { output, err := abi.Pack(method, input) if err != nil { // We have some false positives as we can unpack these type successfully, but not pack them @@ -51,7 +51,7 @@ func unpackPack(abi abi.ABI, method string, inputType []interface{}, input []byt func packUnpack(abi abi.ABI, method string, input []interface{}) bool { if packed, err := abi.Pack(method, input); err == nil { outptr := reflect.New(reflect.TypeOf(input)) - err := abi.Unpack(outptr.Interface(), method, packed) + err := abi.UnpackIntoInterface(outptr.Interface(), method, packed) if err != nil { panic(err) }