Merge pull request #2348 from obscuren/abi-variable-input
accounts/abi: Fixed bytes input accept []byte and variable input support
This commit is contained in:
commit
b3b110bc95
@ -56,17 +56,36 @@ func JSON(reader io.Reader) (ABI, error) {
|
|||||||
func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) {
|
func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) {
|
||||||
method := abi.Methods[name]
|
method := abi.Methods[name]
|
||||||
|
|
||||||
|
// variable input is the output appended at the end of packed
|
||||||
|
// output. This is used for strings and bytes types input.
|
||||||
|
var variableInput []byte
|
||||||
|
|
||||||
var ret []byte
|
var ret []byte
|
||||||
for i, a := range args {
|
for i, a := range args {
|
||||||
input := method.Inputs[i]
|
input := method.Inputs[i]
|
||||||
|
// pack the input
|
||||||
packed, err := input.Type.pack(a)
|
packed, err := input.Type.pack(a)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("`%s` %v", name, err)
|
return nil, fmt.Errorf("`%s` %v", name, err)
|
||||||
}
|
}
|
||||||
ret = append(ret, packed...)
|
|
||||||
|
|
||||||
|
// check for a string or bytes input type
|
||||||
|
switch input.Type.T {
|
||||||
|
case StringTy, BytesTy:
|
||||||
|
// calculate the offset
|
||||||
|
offset := len(method.Inputs)*32 + len(variableInput)
|
||||||
|
// set the offset
|
||||||
|
ret = append(ret, packNum(reflect.ValueOf(offset), UintTy)...)
|
||||||
|
// 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:
|
||||||
|
// append the packed value to the input
|
||||||
|
ret = append(ret, packed...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// append the variable input at the end of the packed input
|
||||||
|
ret = append(ret, variableInput...)
|
||||||
|
|
||||||
return ret, nil
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
@ -365,6 +365,134 @@ func ExampleJSON() {
|
|||||||
// 1f2c40920000000000000000000000000000000000000000000000000000000000000001
|
// 1f2c40920000000000000000000000000000000000000000000000000000000000000001
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestInputVariableInputLength(t *testing.T) {
|
||||||
|
const definition = `[
|
||||||
|
{ "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 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test one string
|
||||||
|
strin := "hello world"
|
||||||
|
strpack, err := abi.Pack("strOne", strin)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := make([]byte, 32)
|
||||||
|
offset[31] = 32
|
||||||
|
length := make([]byte, 32)
|
||||||
|
length[31] = byte(len(strin))
|
||||||
|
value := common.RightPadBytes([]byte(strin), 32)
|
||||||
|
exp := append(offset, append(length, value...)...)
|
||||||
|
|
||||||
|
// ignore first 4 bytes of the output. This is the function identifier
|
||||||
|
strpack = strpack[4:]
|
||||||
|
if !bytes.Equal(strpack, exp) {
|
||||||
|
t.Errorf("expected %x, got %x\n", exp, strpack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test one bytes
|
||||||
|
btspack, err := abi.Pack("bytesOne", []byte(strin))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
// ignore first 4 bytes of the output. This is the function identifier
|
||||||
|
btspack = btspack[4:]
|
||||||
|
if !bytes.Equal(btspack, exp) {
|
||||||
|
t.Errorf("expected %x, got %x\n", exp, btspack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test two strings
|
||||||
|
str1 := "hello"
|
||||||
|
str2 := "world"
|
||||||
|
str2pack, err := abi.Pack("strTwo", str1, str2)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset1 := make([]byte, 32)
|
||||||
|
offset1[31] = 64
|
||||||
|
length1 := make([]byte, 32)
|
||||||
|
length1[31] = byte(len(str1))
|
||||||
|
value1 := common.RightPadBytes([]byte(str1), 32)
|
||||||
|
|
||||||
|
offset2 := make([]byte, 32)
|
||||||
|
offset2[31] = 128
|
||||||
|
length2 := make([]byte, 32)
|
||||||
|
length2[31] = byte(len(str2))
|
||||||
|
value2 := common.RightPadBytes([]byte(str2), 32)
|
||||||
|
|
||||||
|
exp2 := append(offset1, offset2...)
|
||||||
|
exp2 = append(exp2, append(length1, value1...)...)
|
||||||
|
exp2 = append(exp2, append(length2, value2...)...)
|
||||||
|
|
||||||
|
// ignore first 4 bytes of the output. This is the function identifier
|
||||||
|
str2pack = str2pack[4:]
|
||||||
|
if !bytes.Equal(str2pack, exp2) {
|
||||||
|
t.Errorf("expected %x, got %x\n", exp, str2pack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test two strings, first > 32, second < 32
|
||||||
|
str1 = strings.Repeat("a", 33)
|
||||||
|
str2pack, err = abi.Pack("strTwo", str1, str2)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset1 = make([]byte, 32)
|
||||||
|
offset1[31] = 64
|
||||||
|
length1 = make([]byte, 32)
|
||||||
|
length1[31] = byte(len(str1))
|
||||||
|
value1 = common.RightPadBytes([]byte(str1), 64)
|
||||||
|
offset2[31] = 160
|
||||||
|
|
||||||
|
exp2 = append(offset1, offset2...)
|
||||||
|
exp2 = append(exp2, append(length1, value1...)...)
|
||||||
|
exp2 = append(exp2, append(length2, value2...)...)
|
||||||
|
|
||||||
|
// ignore first 4 bytes of the output. This is the function identifier
|
||||||
|
str2pack = str2pack[4:]
|
||||||
|
if !bytes.Equal(str2pack, exp2) {
|
||||||
|
t.Errorf("expected %x, got %x\n", exp, str2pack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// test two strings, first > 32, second >32
|
||||||
|
str1 = strings.Repeat("a", 33)
|
||||||
|
str2 = strings.Repeat("a", 33)
|
||||||
|
str2pack, err = abi.Pack("strTwo", str1, str2)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
offset1 = make([]byte, 32)
|
||||||
|
offset1[31] = 64
|
||||||
|
length1 = make([]byte, 32)
|
||||||
|
length1[31] = byte(len(str1))
|
||||||
|
value1 = common.RightPadBytes([]byte(str1), 64)
|
||||||
|
|
||||||
|
offset2 = make([]byte, 32)
|
||||||
|
offset2[31] = 160
|
||||||
|
length2 = make([]byte, 32)
|
||||||
|
length2[31] = byte(len(str2))
|
||||||
|
value2 = common.RightPadBytes([]byte(str2), 64)
|
||||||
|
|
||||||
|
exp2 = append(offset1, offset2...)
|
||||||
|
exp2 = append(exp2, append(length1, value1...)...)
|
||||||
|
exp2 = append(exp2, append(length2, value2...)...)
|
||||||
|
|
||||||
|
// ignore first 4 bytes of the output. This is the function identifier
|
||||||
|
str2pack = str2pack[4:]
|
||||||
|
if !bytes.Equal(str2pack, exp2) {
|
||||||
|
t.Errorf("expected %x, got %x\n", exp, str2pack)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestBytes(t *testing.T) {
|
func TestBytes(t *testing.T) {
|
||||||
const definition = `[
|
const definition = `[
|
||||||
{ "type" : "function", "name" : "balance", "const" : true, "inputs" : [ { "name" : "address", "type" : "bytes20" } ] },
|
{ "type" : "function", "name" : "balance", "const" : true, "inputs" : [ { "name" : "address", "type" : "bytes20" } ] },
|
||||||
|
@ -23,36 +23,38 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
)
|
)
|
||||||
|
|
||||||
var big_t = reflect.TypeOf(&big.Int{})
|
var (
|
||||||
var ubig_t = reflect.TypeOf(&big.Int{})
|
big_t = reflect.TypeOf(&big.Int{})
|
||||||
var byte_t = reflect.TypeOf(byte(0))
|
ubig_t = reflect.TypeOf(&big.Int{})
|
||||||
var byte_ts = reflect.TypeOf([]byte(nil))
|
byte_t = reflect.TypeOf(byte(0))
|
||||||
var uint_t = reflect.TypeOf(uint(0))
|
byte_ts = reflect.TypeOf([]byte(nil))
|
||||||
var uint8_t = reflect.TypeOf(uint8(0))
|
uint_t = reflect.TypeOf(uint(0))
|
||||||
var uint16_t = reflect.TypeOf(uint16(0))
|
uint8_t = reflect.TypeOf(uint8(0))
|
||||||
var uint32_t = reflect.TypeOf(uint32(0))
|
uint16_t = reflect.TypeOf(uint16(0))
|
||||||
var uint64_t = reflect.TypeOf(uint64(0))
|
uint32_t = reflect.TypeOf(uint32(0))
|
||||||
var int_t = reflect.TypeOf(int(0))
|
uint64_t = reflect.TypeOf(uint64(0))
|
||||||
var int8_t = reflect.TypeOf(int8(0))
|
int_t = reflect.TypeOf(int(0))
|
||||||
var int16_t = reflect.TypeOf(int16(0))
|
int8_t = reflect.TypeOf(int8(0))
|
||||||
var int32_t = reflect.TypeOf(int32(0))
|
int16_t = reflect.TypeOf(int16(0))
|
||||||
var int64_t = reflect.TypeOf(int64(0))
|
int32_t = reflect.TypeOf(int32(0))
|
||||||
var hash_t = reflect.TypeOf(common.Hash{})
|
int64_t = reflect.TypeOf(int64(0))
|
||||||
var address_t = reflect.TypeOf(common.Address{})
|
hash_t = reflect.TypeOf(common.Hash{})
|
||||||
|
address_t = reflect.TypeOf(common.Address{})
|
||||||
|
|
||||||
var uint_ts = reflect.TypeOf([]uint(nil))
|
uint_ts = reflect.TypeOf([]uint(nil))
|
||||||
var uint8_ts = reflect.TypeOf([]uint8(nil))
|
uint8_ts = reflect.TypeOf([]uint8(nil))
|
||||||
var uint16_ts = reflect.TypeOf([]uint16(nil))
|
uint16_ts = reflect.TypeOf([]uint16(nil))
|
||||||
var uint32_ts = reflect.TypeOf([]uint32(nil))
|
uint32_ts = reflect.TypeOf([]uint32(nil))
|
||||||
var uint64_ts = reflect.TypeOf([]uint64(nil))
|
uint64_ts = reflect.TypeOf([]uint64(nil))
|
||||||
var ubig_ts = reflect.TypeOf([]*big.Int(nil))
|
ubig_ts = reflect.TypeOf([]*big.Int(nil))
|
||||||
|
|
||||||
var int_ts = reflect.TypeOf([]int(nil))
|
int_ts = reflect.TypeOf([]int(nil))
|
||||||
var int8_ts = reflect.TypeOf([]int8(nil))
|
int8_ts = reflect.TypeOf([]int8(nil))
|
||||||
var int16_ts = reflect.TypeOf([]int16(nil))
|
int16_ts = reflect.TypeOf([]int16(nil))
|
||||||
var int32_ts = reflect.TypeOf([]int32(nil))
|
int32_ts = reflect.TypeOf([]int32(nil))
|
||||||
var int64_ts = reflect.TypeOf([]int64(nil))
|
int64_ts = reflect.TypeOf([]int64(nil))
|
||||||
var big_ts = reflect.TypeOf([]*big.Int(nil))
|
big_ts = reflect.TypeOf([]*big.Int(nil))
|
||||||
|
)
|
||||||
|
|
||||||
// U256 will ensure unsigned 256bit on big nums
|
// U256 will ensure unsigned 256bit on big nums
|
||||||
func U256(n *big.Int) []byte {
|
func U256(n *big.Int) []byte {
|
||||||
|
@ -163,6 +163,13 @@ func (t Type) String() (out string) {
|
|||||||
return t.stringKind
|
return t.stringKind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// packBytesSlice packs the given bytes as [L, V] as the canonical representation
|
||||||
|
// bytes slice
|
||||||
|
func packBytesSlice(bytes []byte, l int) []byte {
|
||||||
|
len := packNum(reflect.ValueOf(l), UintTy)
|
||||||
|
return append(len, common.RightPadBytes(bytes, (l+31)/32*32)...)
|
||||||
|
}
|
||||||
|
|
||||||
// Test the given input parameter `v` and checks if it matches certain
|
// Test the given input parameter `v` and checks if it matches certain
|
||||||
// criteria
|
// criteria
|
||||||
// * Big integers are checks for ptr types and if the given value is
|
// * Big integers are checks for ptr types and if the given value is
|
||||||
@ -193,8 +200,14 @@ func (t Type) pack(v interface{}) ([]byte, error) {
|
|||||||
if t.Size > -1 && value.Len() > t.Size {
|
if t.Size > -1 && value.Len() > t.Size {
|
||||||
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)
|
||||||
}
|
}
|
||||||
return []byte(common.LeftPadString(t.String(), 32)), 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
|
||||||
|
if t.T == BytesTy {
|
||||||
|
return packBytesSlice(value.Bytes(), value.Len()), nil
|
||||||
|
}
|
||||||
|
|
||||||
if t.Size > -1 && value.Len() > t.Size {
|
if t.Size > -1 && value.Len() > t.Size {
|
||||||
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)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user