From 93f3e15c344aa836afde334d2207575dbf257422 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Fri, 30 Jun 2017 11:53:23 -0500 Subject: [PATCH] accounts/abi: make a better slicing scheme Signed-off-by: RJ Catalano --- accounts/abi/unpack.go | 60 ++++-------- accounts/abi/unpack_test.go | 181 ++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 43 deletions(-) diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go index fc41c88ac7..a76a57cd86 100644 --- a/accounts/abi/unpack.go +++ b/accounts/abi/unpack.go @@ -34,49 +34,15 @@ 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 - if t.Type.IsSlice { + if t.Type.T == SliceTy { // get the offset which determines the start of this array ... offset = int(binary.BigEndian.Uint64(output[index+24 : index+32])) if offset+32 > len(output) { @@ -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) @@ -116,7 +85,7 @@ func toGoSlice(i int, t Argument, output []byte) (interface{}, error) { // set inter to the correct type (cast) switch elem.T { case IntTy, UintTy: - inter = readInteger(t.Type.Kind, returnOutput) + inter = readInteger(elem.Kind, returnOutput) case BoolTy: inter, err = readBool(returnOutput) if err != nil { @@ -128,9 +97,14 @@ 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") } + + //fmt.Printf("type: %T, value: %v\n", inter, inter) + //fmt.Printf("%v\n", elem.stringKind) // append the item to our reflect slice - refSlice = reflect.Append(refSlice, reflect.ValueOf(inter)) + refSlice.Index(i).Set(reflect.ValueOf(inter)) } // return the interface @@ -185,7 +159,7 @@ func readBool(word []byte) (bool, error) { // argument in T. func toGoType(i int, t Argument, output []byte) (interface{}, error) { // we need to treat slices differently - if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy && t.Type.T != FunctionTy { + if t.Type.T == SliceTy || t.Type.T == ArrayTy { return toGoSlice(i, t, output) } diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go index 8e3afee4e6..6f25284e24 100644 --- a/accounts/abi/unpack_test.go +++ b/accounts/abi/unpack_test.go @@ -229,6 +229,187 @@ 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": "uint8[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []uint8{1, 2}, + []uint8{}, + "", + }, + { + `[ { "type": "uint8[2]" } ]`, + common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + [2]uint8{1, 2}, + [2]uint8{}, + "", + }, + { + `[ { "type": "uint16[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []uint16{1, 2}, + []uint16{}, + "", + }, + { + `[ { "type": "uint16[2]" } ]`, + common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + [2]uint16{1, 2}, + [2]uint16{}, + "", + }, + { + `[ { "type": "uint32[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []uint32{1, 2}, + []uint32{}, + "", + }, + { + `[ { "type": "uint32[2]" } ]`, + common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + [2]uint32{1, 2}, + [2]uint32{}, + "", + }, + { + `[ { "type": "uint64[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []uint64{1, 2}, + []uint64{}, + "", + }, + { + `[ { "type": "uint64[2]" } ]`, + common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + [2]uint64{1, 2}, + [2]uint64{}, + "", + }, + { + `[ { "type": "uint256[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []*big.Int{big.NewInt(1), big.NewInt(2)}, + []*big.Int{}, + "", + }, + { + `[ { "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{}, + "", + }, + { + `[ { "type": "int8[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []int8{1, 2}, + []int8{}, + "", + }, + { + `[ { "type": "int8[2]" } ]`, + common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + [2]int8{1, 2}, + [2]int8{}, + "", + }, + { + `[ { "type": "int16[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []int16{1, 2}, + []int16{}, + "", + }, + { + `[ { "type": "int16[2]" } ]`, + common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + [2]int16{1, 2}, + [2]int16{}, + "", + }, + { + `[ { "type": "int32[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []int32{1, 2}, + []int32{}, + "", + }, + { + `[ { "type": "int32[2]" } ]`, + common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + [2]int32{1, 2}, + [2]int32{}, + "", + }, + { + `[ { "type": "int64[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []int64{1, 2}, + []int64{}, + "", + }, + { + `[ { "type": "int64[2]" } ]`, + common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + [2]int64{1, 2}, + [2]int64{}, + "", + }, + { + `[ { "type": "int256[]" } ]`, + common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), + []*big.Int{big.NewInt(1), big.NewInt(2)}, + []*big.Int{}, + "", + }, + { + `[ { "type": "int256[3]" } ]`, + common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003"), + [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)