forked from cerc-io/plugeth
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:
commit
8d8e2248b2
@ -20,6 +20,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
@ -63,9 +64,8 @@ func (abi ABI) pack(method Method, args ...interface{}) ([]byte, error) {
|
||||
return nil, fmt.Errorf("`%s` %v", method.Name, err)
|
||||
}
|
||||
|
||||
// check for a string or bytes input type
|
||||
switch input.Type.T {
|
||||
case StringTy, BytesTy:
|
||||
// check for a slice type (string, bytes, slice)
|
||||
if input.Type.T == StringTy || input.Type.T == BytesTy || input.Type.IsSlice {
|
||||
// calculate the offset
|
||||
offset := len(method.Inputs)*32 + len(variableInput)
|
||||
// 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
|
||||
// will be appended at the end of the input.
|
||||
variableInput = append(variableInput, packed...)
|
||||
default:
|
||||
} else {
|
||||
// append the packed value to the input
|
||||
ret = append(ret, packed...)
|
||||
}
|
||||
@ -117,11 +117,80 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
||||
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
|
||||
// argument in T.
|
||||
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) {
|
||||
return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
|
||||
}
|
||||
|
@ -49,7 +49,9 @@ const jsondata2 = `
|
||||
{ "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" : "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) {
|
||||
@ -57,7 +59,7 @@ func TestType(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if typ.Kind != reflect.Ptr {
|
||||
if typ.Kind != reflect.Uint {
|
||||
t.Error("expected uint32 to have kind Ptr")
|
||||
}
|
||||
|
||||
@ -65,10 +67,10 @@ func TestType(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if typ.Kind != reflect.Slice {
|
||||
t.Error("expected uint32[] to have type slice")
|
||||
if !typ.IsSlice {
|
||||
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")
|
||||
}
|
||||
|
||||
@ -76,13 +78,13 @@ func TestType(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
if typ.Kind != reflect.Slice {
|
||||
t.Error("expected uint32[2] to have kind slice")
|
||||
if !typ.IsSlice {
|
||||
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")
|
||||
}
|
||||
if typ.Size != 2 {
|
||||
if typ.SliceSize != 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")
|
||||
}
|
||||
|
||||
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 {
|
||||
t.Error(err)
|
||||
}
|
||||
@ -202,17 +200,7 @@ func TestTestSlice(t *testing.T) {
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
addr := make([]byte, 20)
|
||||
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)
|
||||
slice := make([]uint64, 2)
|
||||
if _, err := abi.Pack("uint64[2]", slice); err != nil {
|
||||
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))
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
t.FailNow()
|
||||
}
|
||||
|
||||
addr := make([]byte, 20)
|
||||
if _, err := abi.Pack("address", addr); err != nil {
|
||||
t.Error(err)
|
||||
slice := make([]uint8, 2)
|
||||
_, err = abi.Pack("uint64[2]", slice)
|
||||
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 = append(sig, make([]byte, 64)...)
|
||||
sig[35] = 1
|
||||
sig[67] = 2
|
||||
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("slice", []uint32{1, 2})
|
||||
if err != nil {
|
||||
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) {
|
||||
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() {
|
||||
const definition = `[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBar","outputs":[{"name":"","type":"bool"}],"type":"function"}]`
|
||||
|
||||
@ -370,7 +385,7 @@ func TestInputVariableInputLength(t *testing.T) {
|
||||
{ "type" : "function", "name" : "strOne", "const" : true, "inputs" : [ { "name" : "str", "type" : "string" } ] },
|
||||
{ "type" : "function", "name" : "bytesOne", "const" : true, "inputs" : [ { "name" : "str", "type" : "bytes" } ] },
|
||||
{ "type" : "function", "name" : "strTwo", "const" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "str1", "type" : "string" } ] }
|
||||
]`
|
||||
]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
if err != nil {
|
||||
@ -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) {
|
||||
const definition = `[{ "name" : "balance" }]`
|
||||
|
||||
@ -713,12 +699,15 @@ func TestUnmarshal(t *testing.T) {
|
||||
{ "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] },
|
||||
{ "name" : "fixed", "const" : false, "outputs": [ { "type": "bytes32" } ] },
|
||||
{ "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" } ] }]`
|
||||
|
||||
abi, err := JSON(strings.NewReader(definition))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
buff := new(bytes.Buffer)
|
||||
|
||||
// marshal int
|
||||
var Int *big.Int
|
||||
@ -743,7 +732,6 @@ func TestUnmarshal(t *testing.T) {
|
||||
}
|
||||
|
||||
// marshal dynamic bytes max length 32
|
||||
buff := new(bytes.Buffer)
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
||||
bytesOut := common.RightPadBytes([]byte("hello"), 32)
|
||||
@ -862,4 +850,71 @@ func TestUnmarshal(t *testing.T) {
|
||||
if !bytes.Equal(fixed, out[1].([]byte)) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
func isSigned(v reflect.Value) bool {
|
||||
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:
|
||||
return true
|
||||
}
|
||||
|
@ -81,8 +81,4 @@ func TestSigned(t *testing.T) {
|
||||
if !isSigned(reflect.ValueOf(int(10))) {
|
||||
t.Error()
|
||||
}
|
||||
|
||||
if !isSigned(reflect.ValueOf(big.NewInt(10))) {
|
||||
t.Error()
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,9 @@ const (
|
||||
|
||||
// Type is the reflection of the supported argument type
|
||||
type Type struct {
|
||||
IsSlice bool
|
||||
SliceSize int
|
||||
|
||||
Kind reflect.Kind
|
||||
Type reflect.Type
|
||||
Size int
|
||||
@ -47,6 +50,11 @@ type Type struct {
|
||||
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.
|
||||
//
|
||||
// Strings can be in the format of:
|
||||
@ -61,98 +69,87 @@ type Type struct {
|
||||
// address int256 uint256 real[2]
|
||||
func NewType(t string) (typ Type, err error) {
|
||||
// 1. full string 2. type 3. (opt.) is slice 4. (opt.) size
|
||||
freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?")
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
}
|
||||
res := freg.FindAllStringSubmatch(t, -1)[0]
|
||||
var (
|
||||
isslice bool
|
||||
size int
|
||||
)
|
||||
// parse the full representation of the abi-type definition; including:
|
||||
// * full string
|
||||
// * type
|
||||
// * is slice
|
||||
// * slice size
|
||||
res := fullTypeRegex.FindAllStringSubmatch(t, -1)[0]
|
||||
|
||||
// check if type is slice and parse type.
|
||||
switch {
|
||||
case res[3] != "":
|
||||
// err is ignored. Already checked for number through the regexp
|
||||
size, _ = strconv.Atoi(res[3])
|
||||
isslice = true
|
||||
typ.SliceSize, _ = strconv.Atoi(res[3])
|
||||
typ.IsSlice = true
|
||||
case res[2] != "":
|
||||
isslice = true
|
||||
size = -1
|
||||
typ.IsSlice, typ.SliceSize = true, -1
|
||||
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]*)?")
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
// 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 {
|
||||
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
parsedType := treg.FindAllStringSubmatch(res[1], -1)[0]
|
||||
vsize, _ := strconv.Atoi(parsedType[2])
|
||||
vtype := parsedType[1]
|
||||
// substitute canonical representation
|
||||
if vsize == 0 && (vtype == "int" || vtype == "uint") {
|
||||
vsize = 256
|
||||
// varType is the parsed abi type
|
||||
varType := parsedType[1]
|
||||
// substitute canonical integer
|
||||
if varSize == 0 && (varType == "int" || varType == "uint") {
|
||||
varSize = 256
|
||||
t += "256"
|
||||
}
|
||||
|
||||
if isslice {
|
||||
typ.Kind = reflect.Slice
|
||||
typ.Size = size
|
||||
switch vtype {
|
||||
case "int":
|
||||
typ.Type = big_ts
|
||||
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.Size = 256
|
||||
typ.T = IntTy
|
||||
case "uint":
|
||||
typ.Kind = reflect.Ptr
|
||||
typ.Type = ubig_t
|
||||
typ.Size = 256
|
||||
typ.T = UintTy
|
||||
case "bool":
|
||||
typ.Kind = reflect.Bool
|
||||
typ.T = BoolTy
|
||||
case "real": // TODO
|
||||
typ.Kind = reflect.Invalid
|
||||
case "address":
|
||||
typ.Kind = reflect.Slice
|
||||
typ.Type = address_t
|
||||
typ.Size = 20
|
||||
typ.T = AddressTy
|
||||
case "string":
|
||||
typ.Kind = reflect.String
|
||||
typ.Size = -1
|
||||
typ.T = StringTy
|
||||
if vsize > 0 {
|
||||
typ.Size = 32
|
||||
}
|
||||
case "hash":
|
||||
typ.Kind = reflect.Slice
|
||||
switch varType {
|
||||
case "int":
|
||||
typ.Kind = reflect.Int
|
||||
typ.Type = big_t
|
||||
typ.Size = varSize
|
||||
typ.T = IntTy
|
||||
case "uint":
|
||||
typ.Kind = reflect.Uint
|
||||
typ.Type = ubig_t
|
||||
typ.Size = varSize
|
||||
typ.T = UintTy
|
||||
case "bool":
|
||||
typ.Kind = reflect.Bool
|
||||
typ.T = BoolTy
|
||||
case "real": // TODO
|
||||
typ.Kind = reflect.Invalid
|
||||
case "address":
|
||||
typ.Type = address_t
|
||||
typ.Size = 20
|
||||
typ.T = AddressTy
|
||||
case "string":
|
||||
typ.Kind = reflect.String
|
||||
typ.Size = -1
|
||||
typ.T = StringTy
|
||||
if varSize > 0 {
|
||||
typ.Size = 32
|
||||
typ.Type = hash_t
|
||||
typ.T = HashTy
|
||||
case "bytes":
|
||||
typ.Kind = reflect.Slice
|
||||
typ.Type = byte_ts
|
||||
typ.Size = vsize
|
||||
if vsize == 0 {
|
||||
typ.T = BytesTy
|
||||
} else {
|
||||
typ.T = FixedBytesTy
|
||||
}
|
||||
default:
|
||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||
}
|
||||
case "hash":
|
||||
typ.Kind = reflect.Array
|
||||
typ.Size = 32
|
||||
typ.Type = hash_t
|
||||
typ.T = HashTy
|
||||
case "bytes":
|
||||
typ.Kind = reflect.Array
|
||||
typ.Type = byte_ts
|
||||
typ.Size = varSize
|
||||
if varSize == 0 {
|
||||
typ.T = BytesTy
|
||||
} else {
|
||||
typ.T = FixedBytesTy
|
||||
}
|
||||
default:
|
||||
return Type{}, fmt.Errorf("unsupported arg type: %s", t)
|
||||
}
|
||||
typ.stringKind = t
|
||||
|
||||
@ -180,14 +177,26 @@ func (t Type) pack(v interface{}) ([]byte, error) {
|
||||
value := reflect.ValueOf(v)
|
||||
switch kind := value.Kind(); kind {
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
// check input is unsigned
|
||||
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
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
if t.Type != ubig_t {
|
||||
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
|
||||
case reflect.Ptr:
|
||||
// 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
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
|
||||
// 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
|
||||
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.")
|
||||
}
|
||||
|
||||
var packed []byte
|
||||
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
|
||||
}
|
||||
packed = append(packed, val...)
|
||||
}
|
||||
return packed, nil
|
||||
return packBytesSlice(packed, value.Len()), nil
|
||||
case reflect.Bool:
|
||||
if value.Bool() {
|
||||
return common.LeftPadBytes(common.Big1.Bytes(), 32), nil
|
||||
|
Loading…
Reference in New Issue
Block a user