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"
|
"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)
|
||||||
}
|
}
|
||||||
|
@ -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"}]`
|
||||||
|
|
||||||
@ -370,7 +385,7 @@ func TestInputVariableInputLength(t *testing.T) {
|
|||||||
{ "type" : "function", "name" : "strOne", "const" : true, "inputs" : [ { "name" : "str", "type" : "string" } ] },
|
{ "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" : "bytesOne", "const" : true, "inputs" : [ { "name" : "str", "type" : "bytes" } ] },
|
||||||
{ "type" : "function", "name" : "strTwo", "const" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "str1", "type" : "string" } ] }
|
{ "type" : "function", "name" : "strTwo", "const" : true, "inputs" : [ { "name" : "str", "type" : "string" }, { "name" : "str1", "type" : "string" } ] }
|
||||||
]`
|
]`
|
||||||
|
|
||||||
abi, err := JSON(strings.NewReader(definition))
|
abi, err := JSON(strings.NewReader(definition))
|
||||||
if err != nil {
|
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) {
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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,98 +69,87 @@ 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.
|
||||||
if err != nil {
|
parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0]
|
||||||
return Type{}, err
|
// 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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// varType is the parsed abi type
|
||||||
parsedType := treg.FindAllStringSubmatch(res[1], -1)[0]
|
varType := parsedType[1]
|
||||||
vsize, _ := strconv.Atoi(parsedType[2])
|
// substitute canonical integer
|
||||||
vtype := parsedType[1]
|
if varSize == 0 && (varType == "int" || varType == "uint") {
|
||||||
// substitute canonical representation
|
varSize = 256
|
||||||
if vsize == 0 && (vtype == "int" || vtype == "uint") {
|
|
||||||
vsize = 256
|
|
||||||
t += "256"
|
t += "256"
|
||||||
}
|
}
|
||||||
|
|
||||||
if isslice {
|
switch varType {
|
||||||
typ.Kind = reflect.Slice
|
case "int":
|
||||||
typ.Size = size
|
typ.Kind = reflect.Int
|
||||||
switch vtype {
|
typ.Type = big_t
|
||||||
case "int":
|
typ.Size = varSize
|
||||||
typ.Type = big_ts
|
typ.T = IntTy
|
||||||
case "uint":
|
case "uint":
|
||||||
typ.Type = ubig_ts
|
typ.Kind = reflect.Uint
|
||||||
default:
|
typ.Type = ubig_t
|
||||||
return Type{}, fmt.Errorf("unsupported arg slice type: %s", t)
|
typ.Size = varSize
|
||||||
}
|
typ.T = UintTy
|
||||||
} else {
|
case "bool":
|
||||||
switch vtype {
|
typ.Kind = reflect.Bool
|
||||||
case "int":
|
typ.T = BoolTy
|
||||||
typ.Kind = reflect.Ptr
|
case "real": // TODO
|
||||||
typ.Type = big_t
|
typ.Kind = reflect.Invalid
|
||||||
typ.Size = 256
|
case "address":
|
||||||
typ.T = IntTy
|
typ.Type = address_t
|
||||||
case "uint":
|
typ.Size = 20
|
||||||
typ.Kind = reflect.Ptr
|
typ.T = AddressTy
|
||||||
typ.Type = ubig_t
|
case "string":
|
||||||
typ.Size = 256
|
typ.Kind = reflect.String
|
||||||
typ.T = UintTy
|
typ.Size = -1
|
||||||
case "bool":
|
typ.T = StringTy
|
||||||
typ.Kind = reflect.Bool
|
if varSize > 0 {
|
||||||
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
|
|
||||||
typ.Size = 32
|
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
|
typ.stringKind = t
|
||||||
|
|
||||||
@ -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
|
||||||
|
}
|
||||||
|
packed = append(packed, val...)
|
||||||
}
|
}
|
||||||
return packed, nil
|
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
|
||||||
|
Loading…
Reference in New Issue
Block a user