From 2615dc38870a9bce8863e92bbd728f58504f2db7 Mon Sep 17 00:00:00 2001 From: RJ Catalano Date: Fri, 9 Jun 2017 15:19:54 -0500 Subject: [PATCH] accounts/abi: separate unpacking functions into own file and separate unpacking to methods and events Signed-off-by: RJ Catalano --- accounts/abi/abi.go | 299 ++------------------------------------ accounts/abi/method.go | 87 ++++++++++- accounts/abi/reflect.go | 3 +- accounts/abi/unpacking.go | 202 +++++++++++++++++++++++++ 4 files changed, 301 insertions(+), 290 deletions(-) create mode 100644 accounts/abi/unpacking.go diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index 93a7280349..1682e8e2c1 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -68,7 +68,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 } @@ -79,199 +79,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 ( @@ -282,108 +89,24 @@ var ( ) // Unpack output in v according to the abi specification -func (abi ABI) Unpack(v interface{}, name string, output []byte) error { - var isEvent bool - - // 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. - method, ok := abi.Methods[name] - if !ok { - method, ok = abi.Events[name] - if !ok { - return fmt.Errorf("abi: could not find requested method or event %v", name) - } - isEvent = true - } +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) } 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 { diff --git a/accounts/abi/method.go b/accounts/abi/method.go index d56f3bc3dd..b4dcbbafe0 100644 --- a/accounts/abi/method.go +++ b/accounts/abi/method.go @@ -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 diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go index 7970ba8ac1..969c8d17c3 100644 --- a/accounts/abi/reflect.go +++ b/accounts/abi/reflect.go @@ -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 diff --git a/accounts/abi/unpacking.go b/accounts/abi/unpacking.go new file mode 100644 index 0000000000..a617b48199 --- /dev/null +++ b/accounts/abi/unpacking.go @@ -0,0 +1,202 @@ +package abi + +import ( + "encoding/binary" + "fmt" + "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) + } +} + +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) +}