aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--accounts/abi/abi.go72
-rw-r--r--accounts/abi/abi_test.go71
-rw-r--r--accounts/abi/type.go5
3 files changed, 143 insertions, 5 deletions
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 01603b217..b0765e7db 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -20,6 +20,7 @@ import (
"encoding/json"
"fmt"
"io"
+ "math/big"
"reflect"
"strings"
@@ -116,11 +117,80 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return append(method.Id(), arguments...), nil
}
+// toGoSliceType prses the input and casts it to the proper slice defined by the ABI
+// argument in T.
+func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
+ index := i * 32
+ // The slice must, at very least be large enough for the index+32 which is exactly the size required
+ // for the [offset in output, size of offset].
+ 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)
+ }
+
+ // first we need to create a slice of the type
+ var refSlice reflect.Value
+ switch t.Type.T {
+ case IntTy, UintTy, BoolTy: // int, uint, bool can all be of type big int.
+ 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))
+ default: // no other types are supported
+ return nil, fmt.Errorf("abi: unsupported slice type %v", t.Type.T)
+ }
+ // get the offset which determines the start of this array ...
+ offset := int(common.BytesToBig(output[index : index+32]).Uint64())
+ if offset+32 > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
+ }
+
+ slice := output[offset:]
+ // ... starting with the size of the array in elements ...
+ size := int(common.BytesToBig(slice[:32]).Uint64())
+ slice = slice[32:]
+ // ... and make sure that we've at the very least the amount of bytes
+ // available in the buffer.
+ if size*32 > len(slice) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32)
+ }
+
+ // reslice to match the required size
+ slice = slice[:(size * 32)]
+ for i := 0; i < size; i++ {
+ var (
+ inter interface{} // interface type
+ returnOutput = slice[i*32 : i*32+32] // the return output
+ )
+
+ // set inter to the correct type (cast)
+ switch t.Type.T {
+ case IntTy, UintTy:
+ inter = common.BytesToBig(returnOutput)
+ case BoolTy:
+ inter = common.BytesToBig(returnOutput).Uint64() > 0
+ case AddressTy:
+ inter = common.BytesToAddress(returnOutput)
+ case HashTy:
+ inter = common.BytesToHash(returnOutput)
+ }
+ // append the item to our reflect slice
+ refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
+ }
+
+ // return the interface
+ return refSlice.Interface(), nil
+}
+
// toGoType parses the input and casts it to the proper type defined by the ABI
// argument in T.
func toGoType(i int, t Argument, output []byte) (interface{}, error) {
- index := i * 32
+ // we need to treat slices differently
+ if t.Type.Kind == reflect.Slice {
+ return toGoSlice(i, t, output)
+ }
+ index := i * 32
if index+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
}
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index db33face9..a0f0d1034 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -688,12 +688,15 @@ func TestUnmarshal(t *testing.T) {
{ "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] },
{ "name" : "fixed", "const" : false, "outputs": [ { "type": "bytes32" } ] },
{ "name" : "multi", "const" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
+ { "name" : "addressSliceSingle", "const" : false, "outputs": [ { "type": "address[]" } ] },
+ { "name" : "addressSliceDouble", "const" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
{ "name" : "mixedBytes", "const" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
+ buff := new(bytes.Buffer)
// marshal int
var Int *big.Int
@@ -718,7 +721,6 @@ func TestUnmarshal(t *testing.T) {
}
// marshal dynamic bytes max length 32
- buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
bytesOut := common.RightPadBytes([]byte("hello"), 32)
@@ -837,4 +839,71 @@ func TestUnmarshal(t *testing.T) {
if !bytes.Equal(fixed, out[1].([]byte)) {
t.Errorf("expected %x, got %x", fixed, out[1])
}
+
+ // marshal address slice
+ buff.Reset()
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) // offset
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size
+ buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
+
+ var outAddr []common.Address
+ err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
+ if err != nil {
+ t.Fatal("didn't expect error:", err)
+ }
+
+ if len(outAddr) != 1 {
+ t.Fatal("expected 1 item, got", len(outAddr))
+ }
+
+ if outAddr[0] != (common.Address{1}) {
+ t.Errorf("expected %x, got %x", common.Address{1}, outAddr[0])
+ }
+
+ // marshal multiple address slice
+ buff.Reset()
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // offset
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // offset
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size
+ buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // size
+ buff.Write(common.Hex2Bytes("0000000000000000000000000200000000000000000000000000000000000000"))
+ buff.Write(common.Hex2Bytes("0000000000000000000000000300000000000000000000000000000000000000"))
+
+ var outAddrStruct struct {
+ A []common.Address
+ B []common.Address
+ }
+ err = abi.Unpack(&outAddrStruct, "addressSliceDouble", buff.Bytes())
+ if err != nil {
+ t.Fatal("didn't expect error:", err)
+ }
+
+ if len(outAddrStruct.A) != 1 {
+ t.Fatal("expected 1 item, got", len(outAddrStruct.A))
+ }
+
+ if outAddrStruct.A[0] != (common.Address{1}) {
+ t.Errorf("expected %x, got %x", common.Address{1}, outAddrStruct.A[0])
+ }
+
+ if len(outAddrStruct.B) != 2 {
+ t.Fatal("expected 1 item, got", len(outAddrStruct.B))
+ }
+
+ if outAddrStruct.B[0] != (common.Address{2}) {
+ t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0])
+ }
+ if outAddrStruct.B[1] != (common.Address{3}) {
+ t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1])
+ }
+
+ // marshal invalid address slice
+ buff.Reset()
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100"))
+
+ err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
+ if err == nil {
+ t.Fatal("expected error:", err)
+ }
}
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 18cd04672..b7ce6a13b 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -113,7 +113,6 @@ func NewType(t string) (typ Type, err error) {
case "real": // TODO
typ.Kind = reflect.Invalid
case "address":
- typ.Kind = reflect.Slice
typ.Type = address_t
typ.Size = 20
typ.T = AddressTy
@@ -125,12 +124,12 @@ func NewType(t string) (typ Type, err error) {
typ.Size = 32
}
case "hash":
- typ.Kind = reflect.Slice
+ typ.Kind = reflect.Array
typ.Size = 32
typ.Type = hash_t
typ.T = HashTy
case "bytes":
- typ.Kind = reflect.Slice
+ typ.Kind = reflect.Array
typ.Type = byte_ts
typ.Size = vsize
if vsize == 0 {