diff --git a/accounts/abi/numbers.go b/accounts/abi/numbers.go index 5d3efff52e..91f2c5d2a4 100644 --- a/accounts/abi/numbers.go +++ b/accounts/abi/numbers.go @@ -25,22 +25,22 @@ import ( ) var ( - big_t = reflect.TypeOf(big.Int{}) - ubig_t = reflect.TypeOf(big.Int{}) - byte_t = reflect.TypeOf(byte(0)) - byte_ts = reflect.TypeOf([]byte(nil)) - uint_t = reflect.TypeOf(uint(0)) - uint8_t = reflect.TypeOf(uint8(0)) - uint16_t = reflect.TypeOf(uint16(0)) - uint32_t = reflect.TypeOf(uint32(0)) - uint64_t = reflect.TypeOf(uint64(0)) - int_t = reflect.TypeOf(int(0)) - int8_t = reflect.TypeOf(int8(0)) - int16_t = reflect.TypeOf(int16(0)) - int32_t = reflect.TypeOf(int32(0)) - int64_t = reflect.TypeOf(int64(0)) - hash_t = reflect.TypeOf(common.Hash{}) - address_t = reflect.TypeOf(common.Address{}) + big_t = reflect.TypeOf(&big.Int{}) + derefbig_t = reflect.TypeOf(big.Int{}) + byte_t = reflect.TypeOf(byte(0)) + byte_ts = reflect.TypeOf([]byte(nil)) + uint_t = reflect.TypeOf(uint(0)) + uint8_t = reflect.TypeOf(uint8(0)) + uint16_t = reflect.TypeOf(uint16(0)) + uint32_t = reflect.TypeOf(uint32(0)) + uint64_t = reflect.TypeOf(uint64(0)) + int_t = reflect.TypeOf(int(0)) + int8_t = reflect.TypeOf(int8(0)) + int16_t = reflect.TypeOf(int16(0)) + int32_t = reflect.TypeOf(int32(0)) + int64_t = reflect.TypeOf(int64(0)) + hash_t = reflect.TypeOf(common.Hash{}) + address_t = reflect.TypeOf(common.Address{}) uint_ts = reflect.TypeOf([]uint(nil)) uint8_ts = reflect.TypeOf([]uint8(nil)) diff --git a/accounts/abi/pack_test.go b/accounts/abi/pack_test.go index c6cfb56ea0..fcb5c75a85 100644 --- a/accounts/abi/pack_test.go +++ b/accounts/abi/pack_test.go @@ -322,12 +322,12 @@ func TestPack(t *testing.T) { } { typ, err := NewType(test.typ) if err != nil { - t.Fatal("unexpected parse error:", err) + t.Fatalf("%v failed. Unexpected parse error: %v", i, err) } output, err := typ.pack(reflect.ValueOf(test.input)) if err != nil { - t.Fatal("unexpected pack error:", err) + t.Fatalf("%v failed. Unexpected pack error: %v", i, err) } if !bytes.Equal(output, test.output) { diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index 8fa75df07f..de8344188e 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -24,7 +24,7 @@ import ( // indirect recursively dereferences the value until it either gets the value // or finds a big.Int func indirect(v reflect.Value) reflect.Value { - if v.Kind() == reflect.Ptr && v.Elem().Type() != big_t { + if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbig_t { return indirect(v.Elem()) } return v diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go index fc41c88ac7..8163d3886c 100644 --- a/accounts/abi/unpack.go +++ b/accounts/abi/unpack.go @@ -34,45 +34,11 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { if index+32 > len(output) { return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32) } + elem := t.Type.Elem - // first we need to create a slice of the type + // this value will become our slice or our array, depending on the type var refSlice reflect.Value - switch elem.T { - 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)) - 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 - refSlice = reflect.ValueOf([]common.Hash(nil)) - case FixedBytesTy: - refSlice = reflect.ValueOf([][]byte(nil)) - default: // no other types are supported - return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T) - } - var slice []byte var size int var offset int @@ -95,10 +61,13 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { // reslice to match the required size slice = slice[:size*32] - } else if t.Type.IsArray { + // declare our slice + refSlice = reflect.MakeSlice(reflect.SliceOf(elem.Type), size, size) + } else if t.Type.T == ArrayTy { //get the number of elements in the array - size = t.Type.SliceSize - + size = t.Type.Size + // declare our slice + refSlice = reflect.New(reflect.ArrayOf(size, elem.Type)).Elem() //check to make sure array size matches up if index+32*size > len(output) { return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), index+32*size) @@ -128,9 +97,11 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { inter = common.BytesToHash(returnOutput) case FixedBytesTy: inter = returnOutput + default: + return nil, fmt.Errorf("abi: unsupported slice type passed in") } // append the item to our reflect slice - refSlice = reflect.Append(refSlice, reflect.ValueOf(inter)) + refSlice.Index(i).Set(reflect.ValueOf(inter)) } // return the interface diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index 8e3afee4e6..425ea0a4a2 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -229,6 +229,68 @@ func TestSimpleMethodUnpack(t *testing.T) { } } +func TestArraysAndSlicesUnpack(t *testing.T) { + for i, test := range []struct { + def string // definition of the **output** ABI params + marshalledOutput []byte // evm return data + expectedOut interface{} // the expected output + outVar interface{} // the output variable (e.g. uint32, *big.Int, etc) + err string // empty or error if expected + }{ + { + `[ { "type": "uint32[]" } ]`, + append(pad(common.Hex2Bytes("20"), 32, true), formatSliceOutput([]byte{1}, []byte{2})...), + []uint32{1, 2}, + []uint32{}, + "", + }, + { + `[ { "type": "uint32[2]" } ]`, + append(pad([]byte{1}, 32, true), pad([]byte{2}, 32, true)...), + [2]uint32{1, 2}, + [2]uint32{}, + "", + }, + { + `[ { "type": "uint256[3]" } ]`, + append(pad([]byte{1}, 32, true), append(pad([]byte{2}, 32, true), pad([]byte{3}, 32, true)...)...), + [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}, + [3]*big.Int{}, + "", + }, + } { + + abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def) + abi, err := JSON(strings.NewReader(abiDefinition)) + if err != nil { + t.Errorf("%d failed. %v", i, err) + continue + } + + err = abi.Unpack(&test.outVar, "method", test.marshalledOutput) + + if err != nil && len(test.err) == 0 { + t.Errorf("%d failed. Expected no err but got: %v", i, err) + continue + } + if err == nil && len(test.err) != 0 { + t.Errorf("%d failed. Expected err: %v but got none", i, test.err) + continue + } + if err != nil && len(test.err) != 0 && err.Error() != test.err { + t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err) + continue + } + + if err == nil { + if !reflect.DeepEqual(test.expectedOut, test.outVar) { + t.Errorf("%d failed. Output error: expected %v, got %v", i, test.expectedOut, test.outVar) + } + } + + } +} + func TestUnpackSetInterfaceSlice(t *testing.T) { var ( var1 = new(uint8)