diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 3d1010229c..dd843e449e 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -304,12 +304,21 @@ func (abi ABI) Unpack(v interface{}, name string, output []byte) error { // struct will match named return values to the struct's field // names case reflect.Struct: + idx := 0 for i := 0; i < len(method.Outputs); i++ { - marshalledValue, err := toGoType(i, method.Outputs[i], output) + marshalledValue, err := toGoType(idx, method.Outputs[i], output) if err != nil { return err } reflectValue := reflect.ValueOf(marshalledValue) + kind := reflectValue.Type().Kind() + if (kind == reflect.Array || kind == reflect.Slice) && + reflectValue.Type().Elem().Kind() != reflect.Uint8 { + // This is a slice of something other than bytes. + idx += reflectValue.Len() + } else { + idx++ + } for j := 0; j < typ.NumField(); j++ { field := typ.Field(j) diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index a45bd6cc0f..ec99e350d7 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -1002,7 +1002,8 @@ func TestUnmarshal(t *testing.T) { { "name" : "intArraySingle", "constant" : false, "outputs": [ { "type": "uint256[3]" } ] }, { "name" : "addressSliceSingle", "constant" : false, "outputs": [ { "type": "address[]" } ] }, { "name" : "addressSliceDouble", "constant" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] }, - { "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]` + { "name" : "mixedBytes", "constant" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }, + { "name" : "arraysStruct", "constant" : true, "outputs": [ { "name": "a", "type": "bytes4[2]" }, { "name": "b", "type": "bytes20[2]" } ] }]` abi, err := JSON(strings.NewReader(definition)) if err != nil { @@ -1238,4 +1239,37 @@ func TestUnmarshal(t *testing.T) { if err == nil { t.Fatal("expected error:", err) } + + // marshal arrays of arrays + buff.Reset() + buff.Write(common.Hex2Bytes("0000000100000000000000000000000000000000000000000000000000000000")) // a[0] + buff.Write(common.Hex2Bytes("0000000200000000000000000000000000000000000000000000000000000000")) // a[1] + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000003000000000000000000000000")) // b[0] + buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000004000000000000000000000000")) // b[1] + + var arraysStruct struct { + A [2][4]byte + B [2][20]byte + } + err = abi.Unpack(&arraysStruct, "arraysStruct", buff.Bytes()) + if err != nil { + t.Fatal("didn't expect error:", err) + } + + exp4 := [4]byte{0, 0, 0, 1} + if arraysStruct.A[0] != exp4 { + t.Errorf("expected %x, got %x", exp4, arraysStruct.A[0]) + } + exp4 = [4]byte{0, 0, 0, 2} + if arraysStruct.A[1] != exp4 { + t.Errorf("expected %x, got %x", exp4, arraysStruct.A[1]) + } + exp20 := [20]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3} + if arraysStruct.B[0] != exp20 { + t.Errorf("expected %x, got %x", exp20, arraysStruct.B[0]) + } + exp20 = [20]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4} + if arraysStruct.B[1] != exp20 { + t.Errorf("expected %x, got %x", exp20, arraysStruct.B[1]) + } } diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index 7970ba8ac1..c92c10b1be 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -81,7 +81,14 @@ func set(dst, src reflect.Value, output Argument) error { if dst.Len() < output.Type.SliceSize { return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len()) } - reflect.Copy(dst, src) + switch dstType.Elem().Kind() { + case reflect.Array: + for i := 0; i < dst.Len(); i++ { + reflect.Copy(dst.Index(i), src.Index(i)) + } + default: + reflect.Copy(dst, src) + } case dstType.Kind() == reflect.Interface: dst.Set(src) case dstType.Kind() == reflect.Ptr: