mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-17 01:13:45 +00:00
accounts/abi: fixes for static array conversion
Signed-off-by: RJ Catalano <rj@monax.io>
This commit is contained in:
parent
7c26a13cb7
commit
de3d8771cd
3 changed files with 83 additions and 20 deletions
|
|
@ -77,6 +77,7 @@ func (method Method) pack(args ...interface{}) ([]byte, error) {
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unpacks a method return tuple into a struct of corresponding go types
|
||||||
func (method Method) tupleUnpack(v interface{}, output []byte) error {
|
func (method Method) tupleUnpack(v interface{}, output []byte) error {
|
||||||
// make sure the passed value is a pointer
|
// make sure the passed value is a pointer
|
||||||
valueOf := reflect.ValueOf(v)
|
valueOf := reflect.ValueOf(v)
|
||||||
|
|
@ -118,7 +119,7 @@ func (method Method) tupleUnpack(v interface{}, output []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (method Method) tupleReturn() bool { return len(method.Outputs) > 1 }
|
func (method Method) isTupleReturn() bool { return len(method.Outputs) > 1 }
|
||||||
|
|
||||||
func (method Method) singleUnpack(v interface{}, output []byte) error {
|
func (method Method) singleUnpack(v interface{}, output []byte) error {
|
||||||
// make sure the passed value is a pointer
|
// make sure the passed value is a pointer
|
||||||
|
|
|
||||||
|
|
@ -25,12 +25,16 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// unpacker is a utility interface that enables us to have
|
||||||
|
// abstraction between events and methods and also to properly
|
||||||
|
// "unpack" them; e.g. events use Inputs, methods use Outputs.
|
||||||
type unpacker interface {
|
type unpacker interface {
|
||||||
tupleUnpack(v interface{}, output []byte) error
|
tupleUnpack(v interface{}, output []byte) error
|
||||||
singleUnpack(v interface{}, output []byte) error
|
singleUnpack(v interface{}, output []byte) error
|
||||||
tupleReturn() bool
|
isTupleReturn() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reads the integer based on its kind
|
||||||
func readInteger(kind reflect.Kind, b []byte) interface{} {
|
func readInteger(kind reflect.Kind, b []byte) interface{} {
|
||||||
switch kind {
|
switch kind {
|
||||||
case reflect.Uint8:
|
case reflect.Uint8:
|
||||||
|
|
@ -54,6 +58,7 @@ func readInteger(kind reflect.Kind, b []byte) interface{} {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reads a bool
|
||||||
func readBool(word []byte) (bool, error) {
|
func readBool(word []byte) (bool, error) {
|
||||||
if len(word) != 32 {
|
if len(word) != 32 {
|
||||||
return false, fmt.Errorf("abi: fatal error: incorrect word length")
|
return false, fmt.Errorf("abi: fatal error: incorrect word length")
|
||||||
|
|
@ -74,6 +79,8 @@ func readBool(word []byte) (bool, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A function type is simply the address with the function selection signature at the end.
|
||||||
|
// This enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
|
||||||
func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
||||||
if t.T != FunctionTy {
|
if t.T != FunctionTy {
|
||||||
return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array.")
|
return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array.")
|
||||||
|
|
@ -86,6 +93,7 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// through reflection, creates a fixed array to be read from
|
||||||
func readFixedBytes(t Type, word []byte) (interface{}, error) {
|
func readFixedBytes(t Type, word []byte) (interface{}, error) {
|
||||||
if t.T != FixedBytesTy {
|
if t.T != FixedBytesTy {
|
||||||
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array.")
|
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array.")
|
||||||
|
|
@ -98,6 +106,7 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// iteratively unpack elements
|
||||||
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
|
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
|
||||||
if start+32*size > len(output) {
|
if start+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), start+32*size)
|
return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
|
||||||
|
|
@ -106,6 +115,7 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||||
// this value will become our slice or our array, depending on the type
|
// this value will become our slice or our array, depending on the type
|
||||||
var refSlice reflect.Value
|
var refSlice reflect.Value
|
||||||
slice := output[start : start+size*32]
|
slice := output[start : start+size*32]
|
||||||
|
|
||||||
if t.T == SliceTy {
|
if t.T == SliceTy {
|
||||||
// declare our slice
|
// declare our slice
|
||||||
refSlice = reflect.MakeSlice(t.Type, size, size)
|
refSlice = reflect.MakeSlice(t.Type, size, size)
|
||||||
|
|
@ -117,6 +127,10 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, j := start, 0; j*32 < len(slice); i, j = i+32, j+1 {
|
for i, j := start, 0; j*32 < len(slice); i, j = i+32, j+1 {
|
||||||
|
// this corrects the arrangement so that we get all the underlying array values
|
||||||
|
if t.Elem.T == ArrayTy && j != 0 {
|
||||||
|
i = start + t.Elem.Size*32*j
|
||||||
|
}
|
||||||
inter, err := toGoType(i, *t.Elem, output)
|
inter, err := toGoType(i, *t.Elem, output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -129,36 +143,36 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||||
return refSlice.Interface(), nil
|
return refSlice.Interface(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// toGoType parses the input and casts it to the proper type defined by the ABI
|
// toGoType parses the output bytes and recursively assigns the value of these bytes
|
||||||
// argument in T.
|
// into a go type with accordance with the ABI spec.
|
||||||
func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the given index output and check whether we need to read
|
|
||||||
// a different offset and length based on the type (i.e. string, bytes)
|
|
||||||
var (
|
var (
|
||||||
returnOutput []byte
|
returnOutput []byte
|
||||||
i, j int
|
begin, end int
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// if we require a length prefix, find the beginning word and size returned.
|
||||||
if t.requiresLengthPrefix() {
|
if t.requiresLengthPrefix() {
|
||||||
i, j, err = lengthPrefixPointsTo(index, output)
|
begin, end, err = lengthPrefixPointsTo(index, output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
returnOutput = output[index : index+32]
|
returnOutput = output[index : index+32]
|
||||||
}
|
}
|
||||||
|
|
||||||
switch t.T {
|
switch t.T {
|
||||||
case SliceTy:
|
case SliceTy:
|
||||||
return forEachUnpack(t, output, i, j)
|
return forEachUnpack(t, output, begin, end)
|
||||||
case ArrayTy:
|
case ArrayTy:
|
||||||
return forEachUnpack(t, output, i, t.Size)
|
return forEachUnpack(t, output, index, t.Size)
|
||||||
case StringTy: // variable arrays are written at the end of the return bytes
|
case StringTy: // variable arrays are written at the end of the return bytes
|
||||||
return string(output[i : i+j]), nil
|
return string(output[begin : begin+end]), nil
|
||||||
case IntTy, UintTy:
|
case IntTy, UintTy:
|
||||||
return readInteger(t.Kind, returnOutput), nil
|
return readInteger(t.Kind, returnOutput), nil
|
||||||
case BoolTy:
|
case BoolTy:
|
||||||
|
|
@ -168,7 +182,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||||
case HashTy:
|
case HashTy:
|
||||||
return common.BytesToHash(returnOutput), nil
|
return common.BytesToHash(returnOutput), nil
|
||||||
case BytesTy:
|
case BytesTy:
|
||||||
return output[i : i+j], nil
|
return output[begin : begin+end], nil
|
||||||
case FixedBytesTy:
|
case FixedBytesTy:
|
||||||
return readFixedBytes(t, returnOutput)
|
return readFixedBytes(t, returnOutput)
|
||||||
case FunctionTy:
|
case FunctionTy:
|
||||||
|
|
@ -194,6 +208,7 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checks for proper formatting of byte output
|
||||||
func bytesAreProper(output []byte) error {
|
func bytesAreProper(output []byte) error {
|
||||||
if len(output) == 0 {
|
if len(output) == 0 {
|
||||||
return fmt.Errorf("abi: unmarshalling empty output")
|
return fmt.Errorf("abi: unmarshalling empty output")
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSimpleMethodUnpack(t *testing.T) {
|
func TestSingleValueUnpack(t *testing.T) {
|
||||||
for i, test := range []struct {
|
for i, test := range []struct {
|
||||||
def string // definition of the **output** ABI params
|
def string // definition of the **output** ABI params
|
||||||
marshalledOutput []byte // evm return data
|
marshalledOutput []byte // evm return data
|
||||||
|
|
@ -128,6 +128,13 @@ func TestSimpleMethodUnpack(t *testing.T) {
|
||||||
"fixedBytes32",
|
"fixedBytes32",
|
||||||
"abi: cannot unmarshal []uint8 in to [32]uint8",
|
"abi: cannot unmarshal []uint8 in to [32]uint8",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`[ { "type": "bytes32" } ]`,
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
nil,
|
||||||
|
"bytes",
|
||||||
|
"abi: cannot unmarshal [32]uint8 in to []uint8",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
`[ { "type": "bytes32" } ]`,
|
`[ { "type": "bytes32" } ]`,
|
||||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||||
|
|
@ -238,7 +245,7 @@ func TestSimpleMethodUnpack(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
func TestArraysAndSlicesUnpack(t *testing.T) {
|
func TestSingleArraysAndSlicesUnpack(t *testing.T) {
|
||||||
for i, test := range []struct {
|
for i, test := range []struct {
|
||||||
def string // definition of the **output** ABI params
|
def string // definition of the **output** ABI params
|
||||||
marshalledOutput []byte // evm return data
|
marshalledOutput []byte // evm return data
|
||||||
|
|
@ -253,6 +260,14 @@ func TestArraysAndSlicesUnpack(t *testing.T) {
|
||||||
"[]uint8",
|
"[]uint8",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
`[ { "type": "uint8[2]" } ]`,
|
||||||
|
common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
[2]uint8{1, 2},
|
||||||
|
"[2]uint8",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
// multi dimensional, if these pass, all types that don't require length prefix should pass
|
||||||
{
|
{
|
||||||
`[ { "type": "uint8[][]" } ]`,
|
`[ { "type": "uint8[][]" } ]`,
|
||||||
common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
|
@ -261,10 +276,24 @@ func TestArraysAndSlicesUnpack(t *testing.T) {
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
`[ { "type": "uint8[2]" } ]`,
|
`[ { "type": "uint8[2][2]" } ]`,
|
||||||
common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
[2]uint8{1, 2},
|
[2][2]uint8{{1, 2}, {1, 2}},
|
||||||
"[2]uint8",
|
"[2][2]uint8",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`[ { "type": "uint8[][2]" } ]`,
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001"),
|
||||||
|
[2][]uint8{{1}, {1}},
|
||||||
|
"[2][]uint8",
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`[ { "type": "uint8[2][]" } ]`,
|
||||||
|
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||||
|
[][2]uint8{{1, 2}},
|
||||||
|
"[][2]uint8",
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -394,8 +423,6 @@ func TestArraysAndSlicesUnpack(t *testing.T) {
|
||||||
"",
|
"",
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
//t.Log(test.marshalledOutput)
|
|
||||||
|
|
||||||
abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
|
abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
|
||||||
abi, err := JSON(strings.NewReader(abiDefinition))
|
abi, err := JSON(strings.NewReader(abiDefinition))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -409,6 +436,26 @@ func TestArraysAndSlicesUnpack(t *testing.T) {
|
||||||
var v [][]uint8
|
var v [][]uint8
|
||||||
err = abi.Unpack(&v, "method", test.marshalledOutput)
|
err = abi.Unpack(&v, "method", test.marshalledOutput)
|
||||||
outvar = v
|
outvar = v
|
||||||
|
case "[2][2]uint8":
|
||||||
|
var v [2][2]uint8
|
||||||
|
err = abi.Unpack(&v, "method", test.marshalledOutput)
|
||||||
|
outvar = v
|
||||||
|
case "[][2]uint8":
|
||||||
|
var v [][2]uint8
|
||||||
|
err = abi.Unpack(&v, "method", test.marshalledOutput)
|
||||||
|
outvar = v
|
||||||
|
case "[2][]uint8":
|
||||||
|
var v [2][]uint8
|
||||||
|
err = abi.Unpack(&v, "method", test.marshalledOutput)
|
||||||
|
outvar = v
|
||||||
|
case "[2][2][2]uint8":
|
||||||
|
var v [2][2][2]uint8
|
||||||
|
err = abi.Unpack(&v, "method", test.marshalledOutput)
|
||||||
|
outvar = v
|
||||||
|
case "[][][]uint8":
|
||||||
|
var v [][][]uint8
|
||||||
|
err = abi.Unpack(&v, "method", test.marshalledOutput)
|
||||||
|
outvar = v
|
||||||
case "[]uint8":
|
case "[]uint8":
|
||||||
var v []uint8
|
var v []uint8
|
||||||
err = abi.Unpack(&v, "method", test.marshalledOutput)
|
err = abi.Unpack(&v, "method", test.marshalledOutput)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue