forked from cerc-io/plugeth
abi: accept input slices of all supported types
This commit is contained in:
parent
96c7c39ae4
commit
022cbd6800
@ -63,9 +63,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 +72,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...)
|
||||||
}
|
}
|
||||||
|
@ -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) {
|
||||||
@ -68,7 +70,7 @@ func TestType(t *testing.T) {
|
|||||||
if typ.Kind != reflect.Slice {
|
if typ.Kind != reflect.Slice {
|
||||||
t.Error("expected uint32[] to have type slice")
|
t.Error("expected uint32[] to have type 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +81,7 @@ func TestType(t *testing.T) {
|
|||||||
if typ.Kind != reflect.Slice {
|
if typ.Kind != reflect.Slice {
|
||||||
t.Error("expected uint32[2] to have kind slice")
|
t.Error("expected uint32[2] to have kind 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.Size != 2 {
|
||||||
@ -202,16 +204,6 @@ func TestTestSlice(t *testing.T) {
|
|||||||
t.FailNow()
|
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([]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,19 +214,6 @@ func TestTestSlice(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTestAddress(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMethodSignature(t *testing.T) {
|
func TestMethodSignature(t *testing.T) {
|
||||||
String, _ := NewType("string")
|
String, _ := NewType("string")
|
||||||
String32, _ := NewType("string32")
|
String32, _ := NewType("string32")
|
||||||
@ -310,44 +289,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 +497,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" }]`
|
||||||
|
|
||||||
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -96,18 +96,6 @@ func NewType(t string) (typ Type, err error) {
|
|||||||
t += "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 {
|
switch vtype {
|
||||||
case "int":
|
case "int":
|
||||||
typ.Kind = reflect.Ptr
|
typ.Kind = reflect.Ptr
|
||||||
@ -153,6 +141,12 @@ 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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if the type is a slice we must set Kind to a reflect.Slice
|
||||||
|
// so that serialisation can be determined based on this kind.
|
||||||
|
if isslice {
|
||||||
|
typ.Kind = reflect.Slice
|
||||||
|
typ.Size = size
|
||||||
}
|
}
|
||||||
typ.stringKind = t
|
typ.stringKind = t
|
||||||
|
|
||||||
@ -203,7 +197,7 @@ 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
|
||||||
}
|
}
|
||||||
@ -212,21 +206,20 @@ func (t Type) pack(v interface{}) ([]byte, error) {
|
|||||||
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
|
||||||
|
Loading…
Reference in New Issue
Block a user