From 972f0bd3dbd4f6284e66c71b8746c35c10708ccc Mon Sep 17 00:00:00 2001 From: Thomas Bocek Date: Thu, 24 Nov 2016 15:50:45 +0100 Subject: [PATCH 1/4] accounts/abi: support custom int slice types On solidity contract I have "uint32 []" type, when abigen creates Go bindings - they are also "[]uint32" type on Go side. Even though it looks like it should work - the actual type of the data coming from the chain is of type " []*big.Int". When executing contract function from Go side - getting unmarshal error: abi: cannot unmarshal []*big.Int in to []uint32 The fix is to create array with the correct type This fixed the issue reported in: https://github.com/ethereum/go-ethereum/issues/2802 --- accounts/abi/abi.go | 49 +++++++++++++++++++++++++++++++++++++++++--- accounts/abi/type.go | 6 +++++- 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index c3d49da66e..3af8d41c60 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -91,8 +91,31 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { // first we need to create a slice of the type var refSlice reflect.Value switch elem.T { - case IntTy, UintTy, BoolTy: // int, uint, bool can all be of type big int. - refSlice = reflect.ValueOf([]*big.Int(nil)) + case IntTy, UintTy, BoolTy: //we need to create the correct type of array otherwise we see the following issue; + //cannot unmarshal []*big.Int in to []uint32 as described in + //https://github.com/ethereum/go-ethereum/issues/2802 + switch t.Type.Kind { + case reflect.Bool: + refSlice = reflect.ValueOf([]bool(nil)) + case reflect.Uint8: + refSlice = reflect.ValueOf([]uint8(nil)) + case reflect.Uint16: + refSlice = reflect.ValueOf([]uint16(nil)) + case reflect.Uint32: + refSlice = reflect.ValueOf([]uint32(nil)) + case reflect.Uint64: + refSlice = reflect.ValueOf([]uint64(nil)) + case reflect.Int8: + refSlice = reflect.ValueOf([]int8(nil)) + case reflect.Int16: + refSlice = reflect.ValueOf([]int16(nil)) + case reflect.Int32: + refSlice = reflect.ValueOf([]int32(nil)) + case reflect.Int64: + refSlice = reflect.ValueOf([]int64(nil)) + default: + refSlice = reflect.ValueOf([]*big.Int(nil)) + } case AddressTy: // address must be of slice Address refSlice = reflect.ValueOf([]common.Address(nil)) case HashTy: // hash must be of slice hash @@ -147,7 +170,27 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { // set inter to the correct type (cast) switch elem.T { case IntTy, UintTy: - inter = common.BytesToBig(returnOutput) + bigNum := common.BytesToBig(returnOutput) + switch t.Type.Kind { + case reflect.Uint8: + inter = uint8(bigNum.Uint64()) + case reflect.Uint16: + inter = uint16(bigNum.Uint64()) + case reflect.Uint32: + inter = uint32(bigNum.Uint64()) + case reflect.Uint64: + inter = bigNum.Uint64() + case reflect.Int8: + inter = int8(bigNum.Int64()) + case reflect.Int16: + inter = int16(bigNum.Int64()) + case reflect.Int32: + inter = int32(bigNum.Int64()) + case reflect.Int64: + inter = bigNum.Int64() + default: + inter = common.BytesToBig(returnOutput) + } case BoolTy: inter = common.BytesToBig(returnOutput).Uint64() > 0 case AddressTy: diff --git a/accounts/abi/type.go b/accounts/abi/type.go index 2bd341bd25..6fde7bf712 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -91,7 +91,11 @@ func NewType(t string) (typ Type, err error) { } typ.Elem = &sliceType typ.stringKind = sliceType.stringKind + t[len(res[1]):] - return typ, nil + //Altough we know that this is an array, we cannot return as we don't + //know the type of the element, however, if it is still an array, then don't determine the type + if typ.Elem.IsArray { + return typ, nil + } } // parse the type and size of the abi-type. From fc213c873d2e922a606f08a1a6323243da26dd2a Mon Sep 17 00:00:00 2001 From: tbocek Date: Sun, 11 Dec 2016 13:33:51 +0100 Subject: [PATCH 2/4] accounts/abi: added testcase to unpack []uint32 --- accounts/abi/abi_test.go | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 1e5ee0efe5..a45bd6cc0f 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -332,6 +332,30 @@ func TestUnpackSetInterfaceSlice(t *testing.T) { } } +func TestUnpackSetInterfaceArrayOutput(t *testing.T) { + var ( + var1 = new([1]uint32) + var2 = new([1]uint32) + ) + out := []interface{}{var1, var2} + abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint32[1]"}, {"type":"uint32[1]"}]}]`)) + if err != nil { + t.Fatal(err) + } + marshalledReturn := append(pad([]byte{1}, 32, true), pad([]byte{2}, 32, true)...) + err = abi.Unpack(&out, "ints", marshalledReturn) + if err != nil { + t.Fatal(err) + } + + if *var1 != [1]uint32{1} { + t.Error("expected var1 to be [1], got", *var1) + } + if *var2 != [1]uint32{2} { + t.Error("expected var2 to be [2], got", *var2) + } +} + func TestPack(t *testing.T) { for i, test := range []struct { typ string From 47372813efa13e8f92b90e70ab46b69e6792d8c7 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Wed, 14 Dec 2016 23:34:27 +0100 Subject: [PATCH 3/4] accounts/abi: fixed comments --- accounts/abi/abi.go | 5 ++--- accounts/abi/type.go | 5 +++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 3af8d41c60..627a2a0c4c 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -91,9 +91,8 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { // first we need to create a slice of the type var refSlice reflect.Value switch elem.T { - case IntTy, UintTy, BoolTy: //we need to create the correct type of array otherwise we see the following issue; - //cannot unmarshal []*big.Int in to []uint32 as described in - //https://github.com/ethereum/go-ethereum/issues/2802 + case IntTy, UintTy, BoolTy: + // create a new reference slice matching the element type switch t.Type.Kind { case reflect.Bool: refSlice = reflect.ValueOf([]bool(nil)) diff --git a/accounts/abi/type.go b/accounts/abi/type.go index 6fde7bf712..191c299557 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -91,8 +91,9 @@ func NewType(t string) (typ Type, err error) { } typ.Elem = &sliceType typ.stringKind = sliceType.stringKind + t[len(res[1]):] - //Altough we know that this is an array, we cannot return as we don't - //know the type of the element, however, if it is still an array, then don't determine the type + // Altough we know that this is an array, we cannot return + // as we don't know the type of the element, however, if it + // is still an array, then don't determine the type. if typ.Elem.IsArray { return typ, nil } From 1bd9769111133f01faa20b8665c7adf916b6d50a Mon Sep 17 00:00:00 2001 From: Thomas Bocek Date: Thu, 5 Jan 2017 13:42:28 +0100 Subject: [PATCH 4/4] accounts/abi: fixed broken types slice testcases Check for slice in type as well and adapted test case as arrays also store its types. --- accounts/abi/type.go | 9 +++++++-- accounts/abi/type_test.go | 24 ++++++++++++------------ 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/accounts/abi/type.go b/accounts/abi/type.go index 191c299557..ed3e33f395 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -94,7 +94,7 @@ func NewType(t string) (typ Type, err error) { // Altough we know that this is an array, we cannot return // as we don't know the type of the element, however, if it // is still an array, then don't determine the type. - if typ.Elem.IsArray { + if typ.Elem.IsArray || typ.Elem.IsSlice { return typ, nil } } @@ -117,7 +117,12 @@ func NewType(t string) (typ Type, err error) { varSize = 256 t += "256" } - typ.stringKind = t + + // only set stringKind if not array or slice, as for those, + // the correct string type has been set + if !(typ.IsArray || typ.IsSlice) { + typ.stringKind = t + } switch varType { case "int": diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go index bf776cf09d..1557c2a41b 100644 --- a/accounts/abi/type_test.go +++ b/accounts/abi/type_test.go @@ -34,17 +34,17 @@ func TestTypeRegexp(t *testing.T) { {"int", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}}, {"int8", Type{Kind: reflect.Int8, Type: big_t, Size: 8, T: IntTy, stringKind: "int8"}}, {"int256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}}, - {"int[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}}, - {"int[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}}, - {"int32[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}}, - {"int32[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}}, + {"int[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}}, + {"int[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}}, + {"int32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}}, + {"int32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: big_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}}, {"uint", Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}}, {"uint8", Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}}, {"uint256", Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}}, - {"uint[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}}, - {"uint[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}}, - {"uint32[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}}, - {"uint32[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}}, + {"uint[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}}, + {"uint[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: ubig_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}}, + {"uint32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint32, Type: ubig_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}}, + {"uint32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint32, Type: ubig_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: big_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}}, {"bytes", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}}, {"bytes32", Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}}, {"bytes[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}}, @@ -52,11 +52,11 @@ func TestTypeRegexp(t *testing.T) { {"bytes32[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[]"}}, {"bytes32[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: ubig_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[2]"}}, {"string", Type{Kind: reflect.String, Size: -1, T: StringTy, stringKind: "string"}}, - {"string[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.String, Size: -1, T: StringTy, stringKind: "string"}, stringKind: "string[]"}}, - {"string[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{Kind: reflect.String, Size: -1, T: StringTy, stringKind: "string"}, stringKind: "string[2]"}}, + {"string[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[]"}}, + {"string[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[2]"}}, {"address", Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}}, - {"address[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}}, - {"address[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}}, + {"address[]", Type{IsSlice: true, SliceSize: -1,Kind: reflect.Array, Type:address_t, T: AddressTy, Size:20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}}, + {"address[2]", Type{IsArray: true, SliceSize: 2,Kind: reflect.Array, Type:address_t, T: AddressTy, Size:20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}}, // TODO when fixed types are implemented properly // {"fixed", Type{}},