mirror of https://github.com/status-im/op-geth.git
abi: accept output slices of all supported types
This commit is contained in:
parent
022cbd6800
commit
968d8ffe94
|
@ -20,6 +20,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
@ -116,11 +117,80 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
||||||
return append(method.Id(), arguments...), nil
|
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
|
// toGoType parses the input and casts it to the proper type defined by the ABI
|
||||||
// argument in T.
|
// argument in T.
|
||||||
func toGoType(i int, t Argument, output []byte) (interface{}, error) {
|
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) {
|
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)
|
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
|
||||||
}
|
}
|
||||||
|
|
|
@ -688,12 +688,15 @@ func TestUnmarshal(t *testing.T) {
|
||||||
{ "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] },
|
{ "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] },
|
||||||
{ "name" : "fixed", "const" : false, "outputs": [ { "type": "bytes32" } ] },
|
{ "name" : "fixed", "const" : false, "outputs": [ { "type": "bytes32" } ] },
|
||||||
{ "name" : "multi", "const" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
|
{ "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" } ] }]`
|
{ "name" : "mixedBytes", "const" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
buff := new(bytes.Buffer)
|
||||||
|
|
||||||
// marshal int
|
// marshal int
|
||||||
var Int *big.Int
|
var Int *big.Int
|
||||||
|
@ -718,7 +721,6 @@ func TestUnmarshal(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// marshal dynamic bytes max length 32
|
// marshal dynamic bytes max length 32
|
||||||
buff := new(bytes.Buffer)
|
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
||||||
bytesOut := common.RightPadBytes([]byte("hello"), 32)
|
bytesOut := common.RightPadBytes([]byte("hello"), 32)
|
||||||
|
@ -837,4 +839,71 @@ func TestUnmarshal(t *testing.T) {
|
||||||
if !bytes.Equal(fixed, out[1].([]byte)) {
|
if !bytes.Equal(fixed, out[1].([]byte)) {
|
||||||
t.Errorf("expected %x, got %x", fixed, out[1])
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,6 @@ func NewType(t string) (typ Type, err error) {
|
||||||
case "real": // TODO
|
case "real": // TODO
|
||||||
typ.Kind = reflect.Invalid
|
typ.Kind = reflect.Invalid
|
||||||
case "address":
|
case "address":
|
||||||
typ.Kind = reflect.Slice
|
|
||||||
typ.Type = address_t
|
typ.Type = address_t
|
||||||
typ.Size = 20
|
typ.Size = 20
|
||||||
typ.T = AddressTy
|
typ.T = AddressTy
|
||||||
|
@ -125,12 +124,12 @@ func NewType(t string) (typ Type, err error) {
|
||||||
typ.Size = 32
|
typ.Size = 32
|
||||||
}
|
}
|
||||||
case "hash":
|
case "hash":
|
||||||
typ.Kind = reflect.Slice
|
typ.Kind = reflect.Array
|
||||||
typ.Size = 32
|
typ.Size = 32
|
||||||
typ.Type = hash_t
|
typ.Type = hash_t
|
||||||
typ.T = HashTy
|
typ.T = HashTy
|
||||||
case "bytes":
|
case "bytes":
|
||||||
typ.Kind = reflect.Slice
|
typ.Kind = reflect.Array
|
||||||
typ.Type = byte_ts
|
typ.Type = byte_ts
|
||||||
typ.Size = vsize
|
typ.Size = vsize
|
||||||
if vsize == 0 {
|
if vsize == 0 {
|
||||||
|
|
Loading…
Reference in New Issue