Merge pull request #2396 from obscuren/abi-slices

abi: support for input and output slices & removed support for implicit type conversion
This commit is contained in:
Jeffrey Wilcke 2016-04-04 20:42:48 +02:00
commit 8d8e2248b2
5 changed files with 314 additions and 188 deletions

View File

@ -20,6 +20,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"math/big"
"reflect" "reflect"
"strings" "strings"
@ -63,9 +64,8 @@ func (abi ABI) pack(method Method, args ...interface{}) ([]byte, error) {
return nil, fmt.Errorf("`%s` %v", method.Name, err) return nil, fmt.Errorf("`%s` %v", method.Name, err)
} }
// check for a string or bytes input type // check for a slice type (string, bytes, slice)
switch input.Type.T { if input.Type.T == StringTy || input.Type.T == BytesTy || input.Type.IsSlice {
case StringTy, BytesTy:
// calculate the offset // calculate the offset
offset := len(method.Inputs)*32 + len(variableInput) offset := len(method.Inputs)*32 + len(variableInput)
// set the offset // set the offset
@ -73,7 +73,7 @@ func (abi ABI) pack(method Method, args ...interface{}) ([]byte, error) {
// Append the packed output to the variable input. The variable input // Append the packed output to the variable input. The variable input
// will be appended at the end of the input. // will be appended at the end of the input.
variableInput = append(variableInput, packed...) variableInput = append(variableInput, packed...)
default: } else {
// append the packed value to the input // append the packed value to the input
ret = append(ret, packed...) ret = append(ret, packed...)
} }
@ -117,11 +117,80 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return append(method.Id(), arguments...), nil return append(method.Id(), arguments...), nil
} }
// toGoSliceType prses the input and casts it to the proper slice defined by the ABI
// argument in T.
func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
index := i * 32
// The slice must, at very least be large enough for the index+32 which is exactly the size required
// for the [offset in output, size of offset].
if index+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32)
}
// first we need to create a slice of the type
var refSlice reflect.Value
switch t.Type.T {
case IntTy, UintTy, BoolTy: // int, uint, bool can all be of type big int.
refSlice = reflect.ValueOf([]*big.Int(nil))
case AddressTy: // address must be of slice Address
refSlice = reflect.ValueOf([]common.Address(nil))
case HashTy: // hash must be of slice hash
refSlice = reflect.ValueOf([]common.Hash(nil))
default: // no other types are supported
return nil, fmt.Errorf("abi: unsupported slice type %v", t.Type.T)
}
// get the offset which determines the start of this array ...
offset := int(common.BytesToBig(output[index : index+32]).Uint64())
if offset+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
}
slice := output[offset:]
// ... starting with the size of the array in elements ...
size := int(common.BytesToBig(slice[:32]).Uint64())
slice = slice[32:]
// ... and make sure that we've at the very least the amount of bytes
// available in the buffer.
if size*32 > len(slice) {
return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32)
}
// reslice to match the required size
slice = slice[:(size * 32)]
for i := 0; i < size; i++ {
var (
inter interface{} // interface type
returnOutput = slice[i*32 : i*32+32] // the return output
)
// set inter to the correct type (cast)
switch t.Type.T {
case IntTy, UintTy:
inter = common.BytesToBig(returnOutput)
case BoolTy:
inter = common.BytesToBig(returnOutput).Uint64() > 0
case AddressTy:
inter = common.BytesToAddress(returnOutput)
case HashTy:
inter = common.BytesToHash(returnOutput)
}
// append the item to our reflect slice
refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
}
// return the interface
return refSlice.Interface(), nil
}
// toGoType parses the input and casts it to the proper type defined by the ABI // toGoType parses the input and casts it to the proper type defined by the ABI
// argument in T. // argument in T.
func toGoType(i int, t Argument, output []byte) (interface{}, error) { func toGoType(i int, t Argument, output []byte) (interface{}, error) {
index := i * 32 // we need to treat slices differently
if t.Type.IsSlice {
return toGoSlice(i, t, output)
}
index := i * 32
if index+32 > len(output) { if index+32 > len(output) {
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32) return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
} }

View File

@ -49,7 +49,9 @@ const jsondata2 = `
{ "type" : "function", "name" : "foo", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] }, { "type" : "function", "name" : "foo", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] },
{ "type" : "function", "name" : "bar", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] }, { "type" : "function", "name" : "bar", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] },
{ "type" : "function", "name" : "slice", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] }, { "type" : "function", "name" : "slice", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
{ "type" : "function", "name" : "slice256", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] } { "type" : "function", "name" : "slice256", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
{ "type" : "function", "name" : "sliceAddress", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
{ "type" : "function", "name" : "sliceMultiAddress", "const" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] }
]` ]`
func TestType(t *testing.T) { func TestType(t *testing.T) {
@ -57,7 +59,7 @@ func TestType(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if typ.Kind != reflect.Ptr { if typ.Kind != reflect.Uint {
t.Error("expected uint32 to have kind Ptr") t.Error("expected uint32 to have kind Ptr")
} }
@ -65,10 +67,10 @@ func TestType(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if typ.Kind != reflect.Slice { if !typ.IsSlice {
t.Error("expected uint32[] to have type slice") t.Error("expected uint32[] to be slice")
} }
if typ.Type != ubig_ts { if typ.Type != ubig_t {
t.Error("expcted uith32[] to have type uint64") t.Error("expcted uith32[] to have type uint64")
} }
@ -76,13 +78,13 @@ func TestType(t *testing.T) {
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} }
if typ.Kind != reflect.Slice { if !typ.IsSlice {
t.Error("expected uint32[2] to have kind slice") t.Error("expected uint32[2] to be slice")
} }
if typ.Type != ubig_ts { if typ.Type != ubig_t {
t.Error("expcted uith32[2] to have type uint64") t.Error("expcted uith32[2] to have type uint64")
} }
if typ.Size != 2 { if typ.SliceSize != 2 {
t.Error("expected uint32[2] to have a size of 2") t.Error("expected uint32[2] to have a size of 2")
} }
} }
@ -147,10 +149,6 @@ func TestTestNumbers(t *testing.T) {
t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int") t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int")
} }
if _, err := abi.Pack("send", 1000); err != nil {
t.Error("expected send(1000) to cast to big")
}
if _, err := abi.Pack("test", uint32(1000)); err != nil { if _, err := abi.Pack("test", uint32(1000)); err != nil {
t.Error(err) t.Error(err)
} }
@ -202,17 +200,7 @@ func TestTestSlice(t *testing.T) {
t.FailNow() t.FailNow()
} }
addr := make([]byte, 20) slice := make([]uint64, 2)
if _, err := abi.Pack("address", addr); err != nil {
t.Error(err)
}
addr = make([]byte, 21)
if _, err := abi.Pack("address", addr); err == nil {
t.Error("expected address of 21 width to throw")
}
slice := make([]byte, 2)
if _, err := abi.Pack("uint64[2]", slice); err != nil { if _, err := abi.Pack("uint64[2]", slice); err != nil {
t.Error(err) t.Error(err)
} }
@ -222,16 +210,18 @@ func TestTestSlice(t *testing.T) {
} }
} }
func TestTestAddress(t *testing.T) { func TestImplicitTypeCasts(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2)) abi, err := JSON(strings.NewReader(jsondata2))
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.FailNow() t.FailNow()
} }
addr := make([]byte, 20) slice := make([]uint8, 2)
if _, err := abi.Pack("address", addr); err != nil { _, err = abi.Pack("uint64[2]", slice)
t.Error(err) expStr := "`uint64[2]` abi: cannot use type uint8 as type uint64"
if err.Error() != expStr {
t.Errorf("expected %v, got %v", expStr, err)
} }
} }
@ -310,44 +300,69 @@ func TestPackSlice(t *testing.T) {
} }
sig := crypto.Keccak256([]byte("slice(uint32[2])"))[:4] sig := crypto.Keccak256([]byte("slice(uint32[2])"))[:4]
sig = append(sig, make([]byte, 64)...) sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
sig[35] = 1 sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig[67] = 2 sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err := abi.Pack("slice", []uint32{1, 2}) packed, err := abi.Pack("slice", []uint32{1, 2})
if err != nil { if err != nil {
t.Error(err) t.Error(err)
t.FailNow() }
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
var addrA, addrB = common.Address{1}, common.Address{2}
sig = abi.Methods["sliceAddress"].Id()
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrB[:], 32)...)
packed, err = abi.Pack("sliceAddress", []common.Address{addrA, addrB})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
var addrC, addrD = common.Address{3}, common.Address{4}
sig = abi.Methods["sliceMultiAddress"].Id()
sig = append(sig, common.LeftPadBytes([]byte{64}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{160}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrA[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrB[:], 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
packed, err = abi.Pack("sliceMultiAddress", []common.Address{addrA, addrB}, []common.Address{addrC, addrD})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
sig = crypto.Keccak256([]byte("slice256(uint256[2])"))[:4]
sig = append(sig, common.LeftPadBytes([]byte{32}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err = abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)})
if err != nil {
t.Error(err)
} }
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)
} }
} }
func TestPackSliceBig(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata2))
if err != nil {
t.Error(err)
t.FailNow()
}
sig := crypto.Keccak256([]byte("slice256(uint256[2])"))[:4]
sig = append(sig, make([]byte, 64)...)
sig[35] = 1
sig[67] = 2
packed, err := abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)})
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() {
const definition = `[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBar","outputs":[{"name":"","type":"bool"}],"type":"function"}]` const definition = `[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBar","outputs":[{"name":"","type":"bool"}],"type":"function"}]`
@ -493,35 +508,6 @@ func TestInputVariableInputLength(t *testing.T) {
} }
} }
func TestBytes(t *testing.T) {
const definition = `[
{ "type" : "function", "name" : "balance", "const" : true, "inputs" : [ { "name" : "address", "type" : "bytes20" } ] },
{ "type" : "function", "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }
]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
ok := make([]byte, 20)
_, err = abi.Pack("balance", ok)
if err != nil {
t.Error(err)
}
toosmall := make([]byte, 19)
_, err = abi.Pack("balance", toosmall)
if err != nil {
t.Error(err)
}
toobig := make([]byte, 21)
_, err = abi.Pack("balance", toobig)
if err == nil {
t.Error("expected error")
}
}
func TestDefaultFunctionParsing(t *testing.T) { func TestDefaultFunctionParsing(t *testing.T) {
const definition = `[{ "name" : "balance" }]` const definition = `[{ "name" : "balance" }]`
@ -713,12 +699,15 @@ func TestUnmarshal(t *testing.T) {
{ "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] }, { "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] },
{ "name" : "fixed", "const" : false, "outputs": [ { "type": "bytes32" } ] }, { "name" : "fixed", "const" : false, "outputs": [ { "type": "bytes32" } ] },
{ "name" : "multi", "const" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] }, { "name" : "multi", "const" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
{ "name" : "addressSliceSingle", "const" : false, "outputs": [ { "type": "address[]" } ] },
{ "name" : "addressSliceDouble", "const" : false, "outputs": [ { "name": "a", "type": "address[]" }, { "name": "b", "type": "address[]" } ] },
{ "name" : "mixedBytes", "const" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]` { "name" : "mixedBytes", "const" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
buff := new(bytes.Buffer)
// marshal int // marshal int
var Int *big.Int var Int *big.Int
@ -743,7 +732,6 @@ func TestUnmarshal(t *testing.T) {
} }
// marshal dynamic bytes max length 32 // marshal dynamic bytes max length 32
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
bytesOut := common.RightPadBytes([]byte("hello"), 32) bytesOut := common.RightPadBytes([]byte("hello"), 32)
@ -862,4 +850,71 @@ func TestUnmarshal(t *testing.T) {
if !bytes.Equal(fixed, out[1].([]byte)) { if !bytes.Equal(fixed, out[1].([]byte)) {
t.Errorf("expected %x, got %x", fixed, out[1]) t.Errorf("expected %x, got %x", fixed, out[1])
} }
// marshal address slice
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020")) // offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
var outAddr []common.Address
err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
if err != nil {
t.Fatal("didn't expect error:", err)
}
if len(outAddr) != 1 {
t.Fatal("expected 1 item, got", len(outAddr))
}
if outAddr[0] != (common.Address{1}) {
t.Errorf("expected %x, got %x", common.Address{1}, outAddr[0])
}
// marshal multiple address slice
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // size
buff.Write(common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // size
buff.Write(common.Hex2Bytes("0000000000000000000000000200000000000000000000000000000000000000"))
buff.Write(common.Hex2Bytes("0000000000000000000000000300000000000000000000000000000000000000"))
var outAddrStruct struct {
A []common.Address
B []common.Address
}
err = abi.Unpack(&outAddrStruct, "addressSliceDouble", buff.Bytes())
if err != nil {
t.Fatal("didn't expect error:", err)
}
if len(outAddrStruct.A) != 1 {
t.Fatal("expected 1 item, got", len(outAddrStruct.A))
}
if outAddrStruct.A[0] != (common.Address{1}) {
t.Errorf("expected %x, got %x", common.Address{1}, outAddrStruct.A[0])
}
if len(outAddrStruct.B) != 2 {
t.Fatal("expected 1 item, got", len(outAddrStruct.B))
}
if outAddrStruct.B[0] != (common.Address{2}) {
t.Errorf("expected %x, got %x", common.Address{2}, outAddrStruct.B[0])
}
if outAddrStruct.B[1] != (common.Address{3}) {
t.Errorf("expected %x, got %x", common.Address{3}, outAddrStruct.B[1])
}
// marshal invalid address slice
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000100"))
err = abi.Unpack(&outAddr, "addressSliceSingle", buff.Bytes())
if err == nil {
t.Fatal("expected error:", err)
}
} }

View File

@ -117,8 +117,6 @@ func packNum(value reflect.Value, to byte) []byte {
// checks whether the given reflect value is signed. This also works for slices with a number type // checks whether the given reflect value is signed. This also works for slices with a number type
func isSigned(v reflect.Value) bool { func isSigned(v reflect.Value) bool {
switch v.Type() { switch v.Type() {
case ubig_ts, big_ts, big_t, ubig_t:
return true
case int_ts, int8_ts, int16_ts, int32_ts, int64_ts, int_t, int8_t, int16_t, int32_t, int64_t: case int_ts, int8_ts, int16_ts, int32_ts, int64_ts, int_t, int8_t, int16_t, int32_t, int64_t:
return true return true
} }

View File

@ -81,8 +81,4 @@ func TestSigned(t *testing.T) {
if !isSigned(reflect.ValueOf(int(10))) { if !isSigned(reflect.ValueOf(int(10))) {
t.Error() t.Error()
} }
if !isSigned(reflect.ValueOf(big.NewInt(10))) {
t.Error()
}
} }

View File

@ -40,6 +40,9 @@ const (
// 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 bool
SliceSize int
Kind reflect.Kind Kind reflect.Kind
Type reflect.Type Type reflect.Type
Size int Size int
@ -47,6 +50,11 @@ type Type struct {
stringKind string // holds the unparsed string for deriving signatures stringKind string // holds the unparsed string for deriving signatures
} }
var (
fullTypeRegex = regexp.MustCompile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?")
typeRegex = regexp.MustCompile("([a-zA-Z]+)([0-9]*)?")
)
// NewType returns a fully parsed Type given by the input string or an error if it can't be parsed. // NewType returns a fully parsed Type given by the input string or an error if it can't be parsed.
// //
// Strings can be in the format of: // Strings can be in the format of:
@ -61,63 +69,54 @@ type Type struct {
// address int256 uint256 real[2] // address int256 uint256 real[2]
func NewType(t string) (typ Type, err error) { func NewType(t string) (typ Type, err error) {
// 1. full string 2. type 3. (opt.) is slice 4. (opt.) size // 1. full string 2. type 3. (opt.) is slice 4. (opt.) size
freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?") // parse the full representation of the abi-type definition; including:
if err != nil { // * full string
return Type{}, err // * type
} // * is slice
res := freg.FindAllStringSubmatch(t, -1)[0] // * slice size
var ( res := fullTypeRegex.FindAllStringSubmatch(t, -1)[0]
isslice bool
size int // check if type is slice and parse type.
)
switch { switch {
case res[3] != "": case res[3] != "":
// err is ignored. Already checked for number through the regexp // err is ignored. Already checked for number through the regexp
size, _ = strconv.Atoi(res[3]) typ.SliceSize, _ = strconv.Atoi(res[3])
isslice = true typ.IsSlice = true
case res[2] != "": case res[2] != "":
isslice = true typ.IsSlice, typ.SliceSize = true, -1
size = -1
case res[0] == "": case res[0] == "":
return Type{}, fmt.Errorf("type parse error for `%s`", t) return Type{}, fmt.Errorf("abi: type parse error: %s", t)
} }
treg, err := regexp.Compile("([a-zA-Z]+)([0-9]*)?") // parse the type and size of the abi-type.
parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0]
// varSize is the size of the variable
var varSize int
if len(parsedType[2]) > 0 {
var err error
varSize, err = strconv.Atoi(parsedType[2])
if err != nil { if err != nil {
return Type{}, err return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
} }
}
parsedType := treg.FindAllStringSubmatch(res[1], -1)[0] // varType is the parsed abi type
vsize, _ := strconv.Atoi(parsedType[2]) varType := parsedType[1]
vtype := parsedType[1] // substitute canonical integer
// substitute canonical representation if varSize == 0 && (varType == "int" || varType == "uint") {
if vsize == 0 && (vtype == "int" || vtype == "uint") { varSize = 256
vsize = 256
t += "256" t += "256"
} }
if isslice { switch varType {
typ.Kind = reflect.Slice
typ.Size = size
switch vtype {
case "int": case "int":
typ.Type = big_ts typ.Kind = reflect.Int
case "uint":
typ.Type = ubig_ts
default:
return Type{}, fmt.Errorf("unsupported arg slice type: %s", t)
}
} else {
switch vtype {
case "int":
typ.Kind = reflect.Ptr
typ.Type = big_t typ.Type = big_t
typ.Size = 256 typ.Size = varSize
typ.T = IntTy typ.T = IntTy
case "uint": case "uint":
typ.Kind = reflect.Ptr typ.Kind = reflect.Uint
typ.Type = ubig_t typ.Type = ubig_t
typ.Size = 256 typ.Size = varSize
typ.T = UintTy typ.T = UintTy
case "bool": case "bool":
typ.Kind = reflect.Bool typ.Kind = reflect.Bool
@ -125,7 +124,6 @@ func NewType(t string) (typ Type, err error) {
case "real": // TODO case "real": // TODO
typ.Kind = reflect.Invalid typ.Kind = reflect.Invalid
case "address": case "address":
typ.Kind = reflect.Slice
typ.Type = address_t typ.Type = address_t
typ.Size = 20 typ.Size = 20
typ.T = AddressTy typ.T = AddressTy
@ -133,19 +131,19 @@ func NewType(t string) (typ Type, err error) {
typ.Kind = reflect.String typ.Kind = reflect.String
typ.Size = -1 typ.Size = -1
typ.T = StringTy typ.T = StringTy
if vsize > 0 { if varSize > 0 {
typ.Size = 32 typ.Size = 32
} }
case "hash": case "hash":
typ.Kind = reflect.Slice typ.Kind = reflect.Array
typ.Size = 32 typ.Size = 32
typ.Type = hash_t typ.Type = hash_t
typ.T = HashTy typ.T = HashTy
case "bytes": case "bytes":
typ.Kind = reflect.Slice typ.Kind = reflect.Array
typ.Type = byte_ts typ.Type = byte_ts
typ.Size = vsize typ.Size = varSize
if vsize == 0 { if varSize == 0 {
typ.T = BytesTy typ.T = BytesTy
} else { } else {
typ.T = FixedBytesTy typ.T = FixedBytesTy
@ -153,7 +151,6 @@ func NewType(t string) (typ Type, err error) {
default: default:
return Type{}, fmt.Errorf("unsupported arg type: %s", t) return Type{}, fmt.Errorf("unsupported arg type: %s", t)
} }
}
typ.stringKind = t typ.stringKind = t
return return
@ -180,14 +177,26 @@ func (t Type) pack(v interface{}) ([]byte, error) {
value := reflect.ValueOf(v) value := reflect.ValueOf(v)
switch kind := value.Kind(); kind { switch kind := value.Kind(); kind {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
// check input is unsigned
if t.Type != ubig_t { if t.Type != ubig_t {
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) return nil, fmt.Errorf("abi: type mismatch: %s for %T", t.Type, v)
} }
// no implicit type casting
if int(value.Type().Size()*8) != t.Size {
return nil, fmt.Errorf("abi: cannot use type %T as type uint%d", v, t.Size)
}
return packNum(value, t.T), nil return packNum(value, t.T), nil
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if t.Type != ubig_t { if t.Type != ubig_t {
return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v) return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
} }
// no implicit type casting
if int(value.Type().Size()*8) != t.Size {
return nil, fmt.Errorf("abi: cannot use type %T as type uint%d", v, t.Size)
}
return packNum(value, t.T), nil return packNum(value, t.T), nil
case reflect.Ptr: case reflect.Ptr:
// If the value is a ptr do a assign check (only used by // If the value is a ptr do a assign check (only used by
@ -203,30 +212,29 @@ func (t Type) pack(v interface{}) ([]byte, error) {
return packBytesSlice([]byte(value.String()), value.Len()), nil return packBytesSlice([]byte(value.String()), value.Len()), nil
case reflect.Slice: case reflect.Slice:
// if the param is a bytes type, pack the slice up as a string // Byte slice is a special case, it gets treated as a single value
if t.T == BytesTy { if t.T == BytesTy {
return packBytesSlice(value.Bytes(), value.Len()), nil return packBytesSlice(value.Bytes(), value.Len()), nil
} }
if t.Size > -1 && value.Len() > t.Size { if t.SliceSize > -1 && value.Len() > t.SliceSize {
return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size) return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
} }
// Address is a special slice. The slice acts as one rather than a list of elements.
if t.T == AddressTy {
return common.LeftPadBytes(v.([]byte), 32), nil
}
// Signed / Unsigned check // Signed / Unsigned check
if (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) { if value.Type() == big_t && (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) {
return nil, fmt.Errorf("slice of incompatible types.") return nil, fmt.Errorf("slice of incompatible types.")
} }
var packed []byte var packed []byte
for i := 0; i < value.Len(); i++ { for i := 0; i < value.Len(); i++ {
packed = append(packed, packNum(value.Index(i), t.T)...) val, err := t.pack(value.Index(i).Interface())
if err != nil {
return nil, err
} }
return packed, nil packed = append(packed, val...)
}
return packBytesSlice(packed, value.Len()), nil
case reflect.Bool: case reflect.Bool:
if value.Bool() { if value.Bool() {
return common.LeftPadBytes(common.Big1.Bytes(), 32), nil return common.LeftPadBytes(common.Big1.Bytes(), 32), nil