This commit is contained in:
RJ Catalano 2017-06-16 14:12:31 +00:00 committed by GitHub
commit 14b9e3be91
11 changed files with 459 additions and 291 deletions

View file

@ -17,13 +17,10 @@
package abi
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"math/big"
"reflect"
"strings"
"github.com/ethereum/go-ethereum/common"
)
@ -34,7 +31,8 @@ import (
type ABI struct {
Constructor Method
Methods map[string]Method
Events map[string]Event
// todo: make events loggable from an interface that specifies very abstract functions.
Events map[string]Event
}
// JSON returns a parsed ABI interface and error if it failed.
@ -67,7 +65,7 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
}
method = m
}
arguments, err := method.pack(method, args...)
arguments, err := method.pack(args...)
if err != nil {
return nil, err
}
@ -78,199 +76,6 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
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
// assignment.
var (
@ -281,96 +86,25 @@ var (
)
// Unpack output in v according to the abi specification
func (abi ABI) Unpack(v interface{}, name string, output []byte) error {
var method = abi.Methods[name]
func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
if len(output) == 0 {
return fmt.Errorf("abi: unmarshalling empty output")
}
// 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)
}
// first look for name in the methods map, otherwise look in the events map, since there will
// be a name collision for one ABI if there are two of the same names.
// If we can't find it, error.
if method, ok := abi.Methods[name]; ok {
err = method.unpack(v, output)
} else if event, ok := abi.Events[name]; ok {
//err = event.unpackLog(v, output) // to create
err = fmt.Errorf("abi: could not find requested method or event %v", name)
} 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
}
err = fmt.Errorf("abi: could not find requested method or event %v", name)
}
return nil
return
}
func (abi *ABI) UnmarshalJSON(data []byte) error {

View file

@ -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))},
{"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)},
{"bool[]", []bool{false, false}, formatSliceOutput(pad([]byte{0}, 32, true), pad([]byte{0}, 32, true))},
} {
typ, err := NewType(test.typ)
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[]", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] },
{ "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" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
{ "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) {
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() {

View file

@ -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, ",")))))
}
/*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.
}*/

View file

@ -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.")
}
}
}*/

View file

@ -39,7 +39,7 @@ type Method struct {
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
if 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
}
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.
//
// Example

View file

@ -17,6 +17,7 @@
package abi
import (
"fmt"
"reflect"
"github.com/ethereum/go-ethereum/common"
@ -48,6 +49,7 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
if reflectValue.Bool() {
return math.PaddedBigBytes(common.Big1, 32)
} else {
fmt.Printf("Got this for value: %v\n", math.PaddedBigBytes(common.Big0, 32))
return math.PaddedBigBytes(common.Big0, 32)
}
case BytesTy:

48
accounts/abi/parsing.go Normal file
View 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)
}

View file

@ -54,8 +54,9 @@ func reflectIntKind(unsigned bool, size int) reflect.Kind {
return reflect.Uint64
}
return reflect.Int64
default:
return reflect.Ptr
}
return reflect.Ptr
}
// mustArrayToBytesSlice creates a new byte slice with the exact same size as value

View file

@ -29,25 +29,30 @@ const (
BoolTy
StringTy
SliceTy
//StructTy
AddressTy
FixedBytesTy
BytesTy
HashTy
FixedpointTy
FixedPointTy
FunctionTy
)
// Type is the reflection of the supported argument type
type Type struct {
IsSlice, IsArray bool
SliceSize int
// Slice descriptions
IsArray bool //todo: change to IsStatic
IsSlice bool //todo: change to IsDynamic
IsDoublySliced bool //todo: find a better name
SliceSize int
// If applicable (struct, slice), the underlying type
Elem *Type
Kind reflect.Kind
Type reflect.Type
Size int
T byte // Our own type checking
Kind reflect.Kind // corresponding go Kind.
Type reflect.Type // corresponding go Type.
Size int // type size (denotes uint256, uint248, etc.)
T byte // Our own type checking
stringKind string // holds the unparsed string for deriving signatures
}
@ -65,7 +70,7 @@ var (
// string int uint fixed
// string32 int8 uint8 uint[]
// 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 = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
)

204
accounts/abi/unpacking.go Normal file
View 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)
}

View file

@ -127,6 +127,7 @@ func TestPaddedBigBytes(t *testing.T) {
{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: 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 {
if result := PaddedBigBytes(test.num, test.n); !bytes.Equal(result, test.result) {