mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-27 23:26:44 +00:00
Merge 886b51c976 into 431cf2a1e4
This commit is contained in:
commit
14b9e3be91
11 changed files with 459 additions and 291 deletions
|
|
@ -17,13 +17,10 @@
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/binary"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/big"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
@ -34,6 +31,7 @@ import (
|
||||||
type ABI struct {
|
type ABI struct {
|
||||||
Constructor Method
|
Constructor Method
|
||||||
Methods map[string]Method
|
Methods map[string]Method
|
||||||
|
// todo: make events loggable from an interface that specifies very abstract functions.
|
||||||
Events map[string]Event
|
Events map[string]Event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,7 +65,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
||||||
}
|
}
|
||||||
method = m
|
method = m
|
||||||
}
|
}
|
||||||
arguments, err := method.pack(method, args...)
|
arguments, err := method.pack(args...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -78,199 +76,6 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
||||||
return append(method.Id(), arguments...), nil
|
return append(method.Id(), arguments...), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// toGoSliceType parses 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)
|
|
||||||
}
|
|
||||||
elem := t.Type.Elem
|
|
||||||
|
|
||||||
// first we need to create a slice of 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 {
|
|
||||||
// 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) {
|
|
||||||
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(binary.BigEndian.Uint64(slice[24:32]))
|
|
||||||
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]
|
|
||||||
} else if t.Type.IsArray {
|
|
||||||
//get the number of elements in the array
|
|
||||||
size = t.Type.SliceSize
|
|
||||||
|
|
||||||
//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)
|
|
||||||
}
|
|
||||||
//slice is there for a fixed amount of times
|
|
||||||
slice = output[index : index+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 elem.T {
|
|
||||||
case IntTy, UintTy:
|
|
||||||
inter = readInteger(t.Type.Kind, returnOutput)
|
|
||||||
case BoolTy:
|
|
||||||
inter = !allZero(returnOutput)
|
|
||||||
case AddressTy:
|
|
||||||
inter = common.BytesToAddress(returnOutput)
|
|
||||||
case HashTy:
|
|
||||||
inter = common.BytesToHash(returnOutput)
|
|
||||||
case FixedBytesTy:
|
|
||||||
inter = returnOutput
|
|
||||||
}
|
|
||||||
// append the item to our reflect slice
|
|
||||||
refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
|
|
||||||
}
|
|
||||||
|
|
||||||
// return the interface
|
|
||||||
return refSlice.Interface(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func readInteger(kind reflect.Kind, b []byte) interface{} {
|
|
||||||
switch kind {
|
|
||||||
case reflect.Uint8:
|
|
||||||
return uint8(b[len(b)-1])
|
|
||||||
case reflect.Uint16:
|
|
||||||
return binary.BigEndian.Uint16(b[len(b)-2:])
|
|
||||||
case reflect.Uint32:
|
|
||||||
return binary.BigEndian.Uint32(b[len(b)-4:])
|
|
||||||
case reflect.Uint64:
|
|
||||||
return binary.BigEndian.Uint64(b[len(b)-8:])
|
|
||||||
case reflect.Int8:
|
|
||||||
return int8(b[len(b)-1])
|
|
||||||
case reflect.Int16:
|
|
||||||
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
|
|
||||||
case reflect.Int32:
|
|
||||||
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
|
|
||||||
case reflect.Int64:
|
|
||||||
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
|
||||||
default:
|
|
||||||
return new(big.Int).SetBytes(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func allZero(b []byte) bool {
|
|
||||||
for _, byte := range b {
|
|
||||||
if byte != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
// 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 {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 returnOutput []byte
|
|
||||||
switch t.Type.T {
|
|
||||||
case StringTy, BytesTy: // variable arrays are written at the end of the return bytes
|
|
||||||
// parse offset from which we should start reading
|
|
||||||
offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
|
|
||||||
if offset+32 > len(output) {
|
|
||||||
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32)
|
|
||||||
}
|
|
||||||
// parse the size up until we should be reading
|
|
||||||
size := int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
|
|
||||||
if offset+32+size > len(output) {
|
|
||||||
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size)
|
|
||||||
}
|
|
||||||
|
|
||||||
// get the bytes for this return value
|
|
||||||
returnOutput = output[offset+32 : offset+32+size]
|
|
||||||
default:
|
|
||||||
returnOutput = output[index : index+32]
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert the bytes to whatever is specified by the ABI.
|
|
||||||
switch t.Type.T {
|
|
||||||
case IntTy, UintTy:
|
|
||||||
return readInteger(t.Type.Kind, returnOutput), nil
|
|
||||||
case BoolTy:
|
|
||||||
return !allZero(returnOutput), nil
|
|
||||||
case AddressTy:
|
|
||||||
return common.BytesToAddress(returnOutput), nil
|
|
||||||
case HashTy:
|
|
||||||
return common.BytesToHash(returnOutput), nil
|
|
||||||
case BytesTy, FixedBytesTy, FunctionTy:
|
|
||||||
return returnOutput, nil
|
|
||||||
case StringTy:
|
|
||||||
return string(returnOutput), nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("abi: unknown type %v", t.Type.T)
|
|
||||||
}
|
|
||||||
|
|
||||||
// these variable are used to determine certain types during type assertion for
|
// these variable are used to determine certain types during type assertion for
|
||||||
// assignment.
|
// assignment.
|
||||||
var (
|
var (
|
||||||
|
|
@ -281,96 +86,25 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
// Unpack output in v according to the abi specification
|
// Unpack output in v according to the abi specification
|
||||||
func (abi ABI) Unpack(v interface{}, name string, output []byte) error {
|
func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
|
||||||
var method = abi.Methods[name]
|
|
||||||
|
|
||||||
if len(output) == 0 {
|
if len(output) == 0 {
|
||||||
return fmt.Errorf("abi: unmarshalling empty output")
|
return fmt.Errorf("abi: unmarshalling empty output")
|
||||||
}
|
}
|
||||||
|
|
||||||
// make sure the passed value is a pointer
|
// first look for name in the methods map, otherwise look in the events map, since there will
|
||||||
valueOf := reflect.ValueOf(v)
|
// be a name collision for one ABI if there are two of the same names.
|
||||||
if reflect.Ptr != valueOf.Kind() {
|
// If we can't find it, error.
|
||||||
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
if method, ok := abi.Methods[name]; ok {
|
||||||
}
|
err = method.unpack(v, output)
|
||||||
|
} else if event, ok := abi.Events[name]; ok {
|
||||||
var (
|
//err = event.unpackLog(v, output) // to create
|
||||||
value = valueOf.Elem()
|
err = fmt.Errorf("abi: could not find requested method or event %v", name)
|
||||||
typ = value.Type()
|
|
||||||
)
|
|
||||||
|
|
||||||
if len(method.Outputs) > 1 {
|
|
||||||
switch value.Kind() {
|
|
||||||
// struct will match named return values to the struct's field
|
|
||||||
// names
|
|
||||||
case reflect.Struct:
|
|
||||||
for i := 0; i < len(method.Outputs); i++ {
|
|
||||||
marshalledValue, err := toGoType(i, method.Outputs[i], output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
reflectValue := reflect.ValueOf(marshalledValue)
|
|
||||||
|
|
||||||
for j := 0; j < typ.NumField(); j++ {
|
|
||||||
field := typ.Field(j)
|
|
||||||
// TODO read tags: `abi:"fieldName"`
|
|
||||||
if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
|
|
||||||
if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case reflect.Slice:
|
|
||||||
if !value.Type().AssignableTo(r_interSlice) {
|
|
||||||
return fmt.Errorf("abi: cannot marshal tuple in to slice %T (only []interface{} is supported)", v)
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the slice already contains values, set those instead of the interface slice itself.
|
|
||||||
if value.Len() > 0 {
|
|
||||||
if len(method.Outputs) > value.Len() {
|
|
||||||
return fmt.Errorf("abi: cannot marshal in to slices of unequal size (require: %v, got: %v)", len(method.Outputs), value.Len())
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < len(method.Outputs); i++ {
|
|
||||||
marshalledValue, err := toGoType(i, method.Outputs[i], output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
reflectValue := reflect.ValueOf(marshalledValue)
|
|
||||||
if err := set(value.Index(i).Elem(), reflectValue, method.Outputs[i]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a new slice and start appending the unmarshalled
|
|
||||||
// values to the new interface slice.
|
|
||||||
z := reflect.MakeSlice(typ, 0, len(method.Outputs))
|
|
||||||
for i := 0; i < len(method.Outputs); i++ {
|
|
||||||
marshalledValue, err := toGoType(i, method.Outputs[i], output)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
z = reflect.Append(z, reflect.ValueOf(marshalledValue))
|
|
||||||
}
|
|
||||||
value.Set(z)
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
marshalledValue, err := toGoType(0, method.Outputs[0], output)
|
err = fmt.Errorf("abi: could not find requested method or event %v", name)
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (abi *ABI) UnmarshalJSON(data []byte) error {
|
func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
|
|
|
||||||
|
|
@ -370,6 +370,7 @@ func TestPack(t *testing.T) {
|
||||||
{"address[]", []common.Address{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 20, false), pad([]byte{2}, 20, false))},
|
{"address[]", []common.Address{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 20, false), pad([]byte{2}, 20, false))},
|
||||||
{"bytes32[]", []common.Hash{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 32, false), pad([]byte{2}, 32, false))},
|
{"bytes32[]", []common.Hash{{1}, {2}}, formatSliceOutput(pad([]byte{1}, 32, false), pad([]byte{2}, 32, false))},
|
||||||
{"function", [24]byte{1}, pad([]byte{1}, 32, false)},
|
{"function", [24]byte{1}, pad([]byte{1}, 32, false)},
|
||||||
|
{"bool[]", []bool{false, false}, formatSliceOutput(pad([]byte{0}, 32, true), pad([]byte{0}, 32, true))},
|
||||||
} {
|
} {
|
||||||
typ, err := NewType(test.typ)
|
typ, err := NewType(test.typ)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -471,7 +472,8 @@ const jsondata2 = `
|
||||||
{ "type" : "function", "name" : "uint64[2]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
|
{ "type" : "function", "name" : "uint64[2]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] },
|
||||||
{ "type" : "function", "name" : "uint64[]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
|
{ "type" : "function", "name" : "uint64[]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
|
||||||
{ "type" : "function", "name" : "foo", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
|
{ "type" : "function", "name" : "foo", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
|
||||||
{ "type" : "function", "name" : "bar", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
|
{ "type" : "function", "name" : "bar", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "", "type" : "uint16" } ] },
|
||||||
|
{ "type" : "function", "name" : "boolMultiPack", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" }, { "name" : "", "type" : "bool" } ] },
|
||||||
{ "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
|
{ "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
|
||||||
{ "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
|
{ "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
|
||||||
{ "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
|
{ "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
|
||||||
|
|
@ -626,6 +628,22 @@ func TestMultiPack(t *testing.T) {
|
||||||
if !bytes.Equal(packed, sig) {
|
if !bytes.Equal(packed, sig) {
|
||||||
t.Errorf("expected %x got %x", sig, packed)
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sig = crypto.Keccak256([]byte("boolMultiPack(bool,bool)"))[:4]
|
||||||
|
sig = append(sig, make([]byte, 64)...)
|
||||||
|
sig[35] = 0
|
||||||
|
sig[67] = 0
|
||||||
|
|
||||||
|
packed, err = abi.Pack("boolMultiPack", []interface{}{false, false}...)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(packed, sig) {
|
||||||
|
t.Errorf("expected %x got %x", sig, packed)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleJSON() {
|
func ExampleJSON() {
|
||||||
|
|
|
||||||
|
|
@ -44,3 +44,10 @@ func (e Event) Id() common.Hash {
|
||||||
}
|
}
|
||||||
return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ",")))))
|
return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ",")))))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*func (e Event) UnpackLog(v interface{}, data []byte) error {
|
||||||
|
// if not indexed, should be able to unpack normal log
|
||||||
|
|
||||||
|
// if it is indexed, we will not be able to get back the value, rather we will only be able to
|
||||||
|
// search for it.
|
||||||
|
}*/
|
||||||
|
|
|
||||||
|
|
@ -54,3 +54,66 @@ func TestEventId(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*func TestEventUnpacking(t *testing.T) {
|
||||||
|
|
||||||
|
var isErrCorrect = func(expectedErr string, receivedError error) bool {
|
||||||
|
if expectedErr != receivedError.Error() {
|
||||||
|
t.Errorf("Expected error %v, receieved error %v", expectedErr, receivedError)
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range []struct {
|
||||||
|
definition string //abi definition
|
||||||
|
data []byte //log data gotten from the event
|
||||||
|
expectedOutput interface{} // the expected output
|
||||||
|
err string // empty or error if expected
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
`[{"anonymous":false,"inputs":[{"indexed":false,"name":"a","type":"uint256"},{"indexed":true,"name":"b","type":"uint256"},{"indexed":false,"name":"c","type":"uint256"}],"name":"testEvent","type":"event"}]`,
|
||||||
|
append(pad([]byte{1}, 32, true), pad([]byte{3}, 32, true)...),
|
||||||
|
[]interface{}{*big.Int.SetUInt64(int(1)), *big.Int{3}},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`[{"anonymous":false,"inputs":[{"indexed":false,"name":"a","type":"int256"},{"indexed":true,"name":"b","type":"int256"},{"indexed":false,"name":"c","type":"int256"}],"name":"testEvent","type":"event"}]`,
|
||||||
|
append(pad([]byte{1}, 32, true), pad([]byte{3}, 32, true)...),
|
||||||
|
[]interface{}{*big.Int{1}, *big.Int{3}},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`[{"anonymous":false,"inputs":[{"indexed":false,"name":"a","type":"bool"},{"indexed":false,"name":"b","type":"int256"},{"indexed":false,"name":"c","type":"uint256"}],"name":"testEvent","type":"event"}]`,
|
||||||
|
append(pad([]byte{0}, 32, true), append(pad([]byte{1}, 32, true), pad([]byte{3}, 32, true)...)...),
|
||||||
|
[]interface{}{false, *big.Int{1}, *big.Int{3}},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`[{"anonymous":false,"inputs":[{"indexed":false,"name":"a","type":"string"},{"indexed":false,"name":"b","type":"string"},{"indexed":false,"name":"c","type":"string"}],"name":"testEvent","type":"event"}]`,
|
||||||
|
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000e0000000000000000000000000000000000000000000000000000000000000000568656c6c6f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000076675636b696e67000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005776f726c64000000000000000000000000000000000000000000000000000000"),
|
||||||
|
[]interface{}{"bring", "me", "ether"},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
`[{"anonymous":false,"inputs":[{"indexed":true,"name":"a","type":"string"},{"indexed":true,"name":"b","type":"string"},{"indexed":true,"name":"c","type":"string"}],"name":"testEvent","type":"event"}]`,
|
||||||
|
[]byte(nil),
|
||||||
|
[]interface{}{},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
abi, err := JSON(strings.NewReader(test.definition))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var v interface{}
|
||||||
|
if err = abi.Events["method"].UnpackLog(&v, data); err == nil {
|
||||||
|
if !reflect.DeepEqual(test.expectedOutput, v) {
|
||||||
|
t.Errorf("\nabi: %v\ndoes not match exp: %v", abi, exp)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
t.Errorf("abi: Unexpected error %v during event unpacking.")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ type Method struct {
|
||||||
Outputs []Argument
|
Outputs []Argument
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Method) pack(method Method, args ...interface{}) ([]byte, error) {
|
func (method Method) pack(args ...interface{}) ([]byte, error) {
|
||||||
// Make sure arguments match up and pack them
|
// Make sure arguments match up and pack them
|
||||||
if len(args) != len(method.Inputs) {
|
if len(args) != len(method.Inputs) {
|
||||||
return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs))
|
return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs))
|
||||||
|
|
@ -77,6 +77,91 @@ func (m Method) pack(method Method, args ...interface{}) ([]byte, error) {
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (method Method) unpack(v interface{}, output []byte) error {
|
||||||
|
// make sure the passed value is a pointer
|
||||||
|
valueOf := reflect.ValueOf(v)
|
||||||
|
if reflect.Ptr != valueOf.Kind() {
|
||||||
|
return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
value = valueOf.Elem()
|
||||||
|
typ = value.Type()
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(method.Outputs) > 1 {
|
||||||
|
switch value.Kind() {
|
||||||
|
// struct will match named return values to the struct's field
|
||||||
|
// names
|
||||||
|
case reflect.Struct:
|
||||||
|
for i := 0; i < len(method.Outputs); i++ {
|
||||||
|
marshalledValue, err := toGoType(i, method.Outputs[i], output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reflectValue := reflect.ValueOf(marshalledValue)
|
||||||
|
|
||||||
|
for j := 0; j < typ.NumField(); j++ {
|
||||||
|
field := typ.Field(j)
|
||||||
|
// TODO read tags: `abi:"fieldName"`
|
||||||
|
if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
|
||||||
|
if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case reflect.Slice:
|
||||||
|
if !value.Type().AssignableTo(r_interSlice) {
|
||||||
|
return fmt.Errorf("abi: cannot marshal tuple in to slice %T (only []interface{} is supported)", v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the slice already contains values, set those instead of the interface slice itself.
|
||||||
|
if value.Len() > 0 {
|
||||||
|
if len(method.Outputs) > value.Len() {
|
||||||
|
return fmt.Errorf("abi: cannot marshal in to slices of unequal size (require: %v, got: %v)", len(method.Outputs), value.Len())
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(method.Outputs); i++ {
|
||||||
|
marshalledValue, err := toGoType(i, method.Outputs[i], output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
reflectValue := reflect.ValueOf(marshalledValue)
|
||||||
|
if err := set(value.Index(i).Elem(), reflectValue, method.Outputs[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a new slice and start appending the unmarshalled
|
||||||
|
// values to the new interface slice.
|
||||||
|
z := reflect.MakeSlice(typ, 0, len(method.Outputs))
|
||||||
|
for i := 0; i < len(method.Outputs); i++ {
|
||||||
|
marshalledValue, err := toGoType(i, method.Outputs[i], output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
z = reflect.Append(z, reflect.ValueOf(marshalledValue))
|
||||||
|
}
|
||||||
|
value.Set(z)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
marshalledValue, err := toGoType(0, method.Outputs[0], output)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// Sig returns the methods string signature according to the ABI spec.
|
// Sig returns the methods string signature according to the ABI spec.
|
||||||
//
|
//
|
||||||
// Example
|
// Example
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package abi
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -48,6 +49,7 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
|
||||||
if reflectValue.Bool() {
|
if reflectValue.Bool() {
|
||||||
return math.PaddedBigBytes(common.Big1, 32)
|
return math.PaddedBigBytes(common.Big1, 32)
|
||||||
} else {
|
} else {
|
||||||
|
fmt.Printf("Got this for value: %v\n", math.PaddedBigBytes(common.Big0, 32))
|
||||||
return math.PaddedBigBytes(common.Big0, 32)
|
return math.PaddedBigBytes(common.Big0, 32)
|
||||||
}
|
}
|
||||||
case BytesTy:
|
case BytesTy:
|
||||||
|
|
|
||||||
48
accounts/abi/parsing.go
Normal file
48
accounts/abi/parsing.go
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
// Copyright 2016 The go-ethereum Authors
|
||||||
|
// This file is part of the go-ethereum library.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
//
|
||||||
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License
|
||||||
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// separates byte slice into a slice of 32 byte slices.
|
||||||
|
func chunkBytes(output []byte) (chunked [][32]byte) {
|
||||||
|
for i, j := 0, 0; i < len(output); i, j = i+32, j+1 {
|
||||||
|
copy(chunked[j][:], output[i:i+32])
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// check to see that the bytes are correctly formatted to 32 byte slices
|
||||||
|
func bytesAreProper(output []byte) error {
|
||||||
|
if len(output)%32 != 0 {
|
||||||
|
return fmt.Errorf("abi: improper length of byte slice detected in output.")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
|
||||||
|
func offsetPointsTo(offset [32]byte) uint {
|
||||||
|
return uint(binary.BigEndian.Uint64(offset[24:32])) / 32
|
||||||
|
}
|
||||||
|
|
||||||
|
// gives the starting and ending indices in the chunked byte slice for the values of an array
|
||||||
|
func parseSliceSize(size [32]byte, currentLength uint) (start uint, end uint) {
|
||||||
|
return currentLength + 1, currentLength + uint(binary.BigEndian.Uint64(size[24:32])/32)
|
||||||
|
}
|
||||||
|
|
@ -54,8 +54,9 @@ func reflectIntKind(unsigned bool, size int) reflect.Kind {
|
||||||
return reflect.Uint64
|
return reflect.Uint64
|
||||||
}
|
}
|
||||||
return reflect.Int64
|
return reflect.Int64
|
||||||
}
|
default:
|
||||||
return reflect.Ptr
|
return reflect.Ptr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// mustArrayToBytesSlice creates a new byte slice with the exact same size as value
|
// mustArrayToBytesSlice creates a new byte slice with the exact same size as value
|
||||||
|
|
|
||||||
|
|
@ -29,24 +29,29 @@ const (
|
||||||
BoolTy
|
BoolTy
|
||||||
StringTy
|
StringTy
|
||||||
SliceTy
|
SliceTy
|
||||||
|
//StructTy
|
||||||
AddressTy
|
AddressTy
|
||||||
FixedBytesTy
|
FixedBytesTy
|
||||||
BytesTy
|
BytesTy
|
||||||
HashTy
|
HashTy
|
||||||
FixedpointTy
|
FixedPointTy
|
||||||
FunctionTy
|
FunctionTy
|
||||||
)
|
)
|
||||||
|
|
||||||
// Type is the reflection of the supported argument type
|
// Type is the reflection of the supported argument type
|
||||||
type Type struct {
|
type Type struct {
|
||||||
IsSlice, IsArray bool
|
// Slice descriptions
|
||||||
|
IsArray bool //todo: change to IsStatic
|
||||||
|
IsSlice bool //todo: change to IsDynamic
|
||||||
|
IsDoublySliced bool //todo: find a better name
|
||||||
SliceSize int
|
SliceSize int
|
||||||
|
|
||||||
|
// If applicable (struct, slice), the underlying type
|
||||||
Elem *Type
|
Elem *Type
|
||||||
|
|
||||||
Kind reflect.Kind
|
Kind reflect.Kind // corresponding go Kind.
|
||||||
Type reflect.Type
|
Type reflect.Type // corresponding go Type.
|
||||||
Size int
|
Size int // type size (denotes uint256, uint248, etc.)
|
||||||
T byte // Our own type checking
|
T byte // Our own type checking
|
||||||
|
|
||||||
stringKind string // holds the unparsed string for deriving signatures
|
stringKind string // holds the unparsed string for deriving signatures
|
||||||
|
|
@ -65,7 +70,7 @@ var (
|
||||||
// string int uint fixed
|
// string int uint fixed
|
||||||
// string32 int8 uint8 uint[]
|
// string32 int8 uint8 uint[]
|
||||||
// address int256 uint256 fixed128x128[2]
|
// address int256 uint256 fixed128x128[2]
|
||||||
fullTypeRegex = regexp.MustCompile(`([a-zA-Z0-9]+)(\[([0-9]*)\])?`)
|
fullTypeRegex = regexp.MustCompile(`([a-zA-Z0-9]+)(\[([0-9]*)\])?(\[([0-9]*)\])?`)
|
||||||
// typeRegex parses the abi sub types
|
// typeRegex parses the abi sub types
|
||||||
typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
|
typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
204
accounts/abi/unpacking.go
Normal file
204
accounts/abi/unpacking.go
Normal file
|
|
@ -0,0 +1,204 @@
|
||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// toGoSliceType parses 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)
|
||||||
|
}
|
||||||
|
elem := t.Type.Elem
|
||||||
|
|
||||||
|
// first we need to create a slice of 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 {
|
||||||
|
// 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) {
|
||||||
|
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(binary.BigEndian.Uint64(slice[24:32]))
|
||||||
|
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]
|
||||||
|
} else if t.Type.IsArray {
|
||||||
|
//get the number of elements in the array
|
||||||
|
size = t.Type.SliceSize
|
||||||
|
|
||||||
|
//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)
|
||||||
|
}
|
||||||
|
//slice is there for a fixed amount of times
|
||||||
|
slice = output[index : index+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 elem.T {
|
||||||
|
case IntTy, UintTy:
|
||||||
|
inter = readInteger(t.Type.Kind, returnOutput)
|
||||||
|
case BoolTy:
|
||||||
|
inter = !allZero(returnOutput)
|
||||||
|
case AddressTy:
|
||||||
|
inter = common.BytesToAddress(returnOutput)
|
||||||
|
case HashTy:
|
||||||
|
inter = common.BytesToHash(returnOutput)
|
||||||
|
case FixedBytesTy:
|
||||||
|
inter = returnOutput
|
||||||
|
}
|
||||||
|
// append the item to our reflect slice
|
||||||
|
refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the interface
|
||||||
|
return refSlice.Interface(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func readInteger(kind reflect.Kind, b []byte) interface{} {
|
||||||
|
switch kind {
|
||||||
|
case reflect.Uint8:
|
||||||
|
return uint8(b[len(b)-1])
|
||||||
|
case reflect.Uint16:
|
||||||
|
return binary.BigEndian.Uint16(b[len(b)-2:])
|
||||||
|
case reflect.Uint32:
|
||||||
|
return binary.BigEndian.Uint32(b[len(b)-4:])
|
||||||
|
case reflect.Uint64:
|
||||||
|
return binary.BigEndian.Uint64(b[len(b)-8:])
|
||||||
|
case reflect.Int8:
|
||||||
|
return int8(b[len(b)-1])
|
||||||
|
case reflect.Int16:
|
||||||
|
return int16(binary.BigEndian.Uint16(b[len(b)-2:]))
|
||||||
|
case reflect.Int32:
|
||||||
|
return int32(binary.BigEndian.Uint32(b[len(b)-4:]))
|
||||||
|
case reflect.Int64:
|
||||||
|
return int64(binary.BigEndian.Uint64(b[len(b)-8:]))
|
||||||
|
default:
|
||||||
|
return new(big.Int).SetBytes(b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// todo: this is inefficient for a bool, just look at the last cell, save yourself 32 iterations
|
||||||
|
func allZero(b []byte) bool {
|
||||||
|
for _, byte := range b {
|
||||||
|
if byte != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// 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 {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 returnOutput []byte
|
||||||
|
switch t.Type.T {
|
||||||
|
case StringTy, BytesTy: // variable arrays are written at the end of the return bytes
|
||||||
|
// parse offset from which we should start reading
|
||||||
|
offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
|
||||||
|
if offset+32 > len(output) {
|
||||||
|
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32)
|
||||||
|
}
|
||||||
|
// parse the size up until we should be reading
|
||||||
|
size := int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
|
||||||
|
if offset+32+size > len(output) {
|
||||||
|
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the bytes for this return value
|
||||||
|
returnOutput = output[offset+32 : offset+32+size]
|
||||||
|
default:
|
||||||
|
returnOutput = output[index : index+32]
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert the bytes to whatever is specified by the ABI.
|
||||||
|
switch t.Type.T {
|
||||||
|
case IntTy, UintTy:
|
||||||
|
return readInteger(t.Type.Kind, returnOutput), nil
|
||||||
|
case BoolTy:
|
||||||
|
return !allZero(returnOutput), nil
|
||||||
|
case AddressTy:
|
||||||
|
return common.BytesToAddress(returnOutput), nil
|
||||||
|
case HashTy:
|
||||||
|
return common.BytesToHash(returnOutput), nil
|
||||||
|
case BytesTy, FixedBytesTy, FunctionTy:
|
||||||
|
return returnOutput, nil
|
||||||
|
case StringTy:
|
||||||
|
return string(returnOutput), nil
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("abi: unknown type %v", t.Type.T)
|
||||||
|
}
|
||||||
|
|
@ -127,6 +127,7 @@ func TestPaddedBigBytes(t *testing.T) {
|
||||||
{num: big.NewInt(1), n: 4, result: []byte{0, 0, 0, 1}},
|
{num: big.NewInt(1), n: 4, result: []byte{0, 0, 0, 1}},
|
||||||
{num: big.NewInt(512), n: 4, result: []byte{0, 0, 2, 0}},
|
{num: big.NewInt(512), n: 4, result: []byte{0, 0, 2, 0}},
|
||||||
{num: BigPow(2, 32), n: 4, result: []byte{1, 0, 0, 0, 0}},
|
{num: BigPow(2, 32), n: 4, result: []byte{1, 0, 0, 0, 0}},
|
||||||
|
{num: big.NewInt(0), n: 32, result: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||||
}
|
}
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
if result := PaddedBigBytes(test.num, test.n); !bytes.Equal(result, test.result) {
|
if result := PaddedBigBytes(test.num, test.n); !bytes.Equal(result, test.result) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue