aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeffrey Wilcke <jeffrey@ethereum.org>2016-03-15 21:06:12 +0800
committerJeffrey Wilcke <jeffrey@ethereum.org>2016-03-15 21:10:39 +0800
commitfe45210c552f5de2ec6293817dc0363a34d0ebfb (patch)
tree261ca0a2ec9582f7d9b7d2d3758cf13bf506a5e8
parente189fb839c688b418b43ad6533111c246c109a93 (diff)
downloaddexon-fe45210c552f5de2ec6293817dc0363a34d0ebfb.tar
dexon-fe45210c552f5de2ec6293817dc0363a34d0ebfb.tar.gz
dexon-fe45210c552f5de2ec6293817dc0363a34d0ebfb.tar.bz2
dexon-fe45210c552f5de2ec6293817dc0363a34d0ebfb.tar.lz
dexon-fe45210c552f5de2ec6293817dc0363a34d0ebfb.tar.xz
dexon-fe45210c552f5de2ec6293817dc0363a34d0ebfb.tar.zst
dexon-fe45210c552f5de2ec6293817dc0363a34d0ebfb.zip
accounts/abi: Fixed bytes input accept []byte and variable input support
Fixed up `[]byte` slice support such that `function print(bytes input)` accepts `[]byte` as input and treats it as 1 element rather than a slice of multiple elements. Added support for variable length input parameters like `bytes` and `strings`.
-rw-r--r--accounts/abi/abi.go23
-rw-r--r--accounts/abi/abi_test.go128
-rw-r--r--accounts/abi/numbers.go58
-rw-r--r--accounts/abi/type.go15
4 files changed, 193 insertions, 31 deletions
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 324d3c76f..e299d43c8 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -56,17 +56,36 @@ func JSON(reader io.Reader) (ABI, error) {
func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) {
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
for i, a := range args {
input := method.Inputs[i]
-
+ // pack the input
packed, err := input.Type.pack(a)
if err != nil {
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
}
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index d1b8330e3..170f3f74b 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -365,6 +365,134 @@ func ExampleJSON() {
// 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) {
const definition = `[
{ "type" : "function", "name" : "balance", "const" : true, "inputs" : [ { "name" : "address", "type" : "bytes20" } ] },
diff --git a/accounts/abi/numbers.go b/accounts/abi/numbers.go
index c37cd5f68..02609d567 100644
--- a/accounts/abi/numbers.go
+++ b/accounts/abi/numbers.go
@@ -23,36 +23,38 @@ import (
"github.com/ethereum/go-ethereum/common"
)
-var big_t = reflect.TypeOf(&big.Int{})
-var ubig_t = reflect.TypeOf(&big.Int{})
-var byte_t = reflect.TypeOf(byte(0))
-var byte_ts = reflect.TypeOf([]byte(nil))
-var uint_t = reflect.TypeOf(uint(0))
-var uint8_t = reflect.TypeOf(uint8(0))
-var uint16_t = reflect.TypeOf(uint16(0))
-var uint32_t = reflect.TypeOf(uint32(0))
-var uint64_t = reflect.TypeOf(uint64(0))
-var int_t = reflect.TypeOf(int(0))
-var int8_t = reflect.TypeOf(int8(0))
-var int16_t = reflect.TypeOf(int16(0))
-var int32_t = reflect.TypeOf(int32(0))
-var int64_t = reflect.TypeOf(int64(0))
-var hash_t = reflect.TypeOf(common.Hash{})
-var address_t = reflect.TypeOf(common.Address{})
+var (
+ big_t = reflect.TypeOf(&big.Int{})
+ ubig_t = reflect.TypeOf(&big.Int{})
+ byte_t = reflect.TypeOf(byte(0))
+ byte_ts = reflect.TypeOf([]byte(nil))
+ uint_t = reflect.TypeOf(uint(0))
+ uint8_t = reflect.TypeOf(uint8(0))
+ uint16_t = reflect.TypeOf(uint16(0))
+ uint32_t = reflect.TypeOf(uint32(0))
+ uint64_t = reflect.TypeOf(uint64(0))
+ int_t = reflect.TypeOf(int(0))
+ int8_t = reflect.TypeOf(int8(0))
+ int16_t = reflect.TypeOf(int16(0))
+ int32_t = reflect.TypeOf(int32(0))
+ int64_t = reflect.TypeOf(int64(0))
+ hash_t = reflect.TypeOf(common.Hash{})
+ address_t = reflect.TypeOf(common.Address{})
-var uint_ts = reflect.TypeOf([]uint(nil))
-var uint8_ts = reflect.TypeOf([]uint8(nil))
-var uint16_ts = reflect.TypeOf([]uint16(nil))
-var uint32_ts = reflect.TypeOf([]uint32(nil))
-var uint64_ts = reflect.TypeOf([]uint64(nil))
-var ubig_ts = reflect.TypeOf([]*big.Int(nil))
+ uint_ts = reflect.TypeOf([]uint(nil))
+ uint8_ts = reflect.TypeOf([]uint8(nil))
+ uint16_ts = reflect.TypeOf([]uint16(nil))
+ uint32_ts = reflect.TypeOf([]uint32(nil))
+ uint64_ts = reflect.TypeOf([]uint64(nil))
+ ubig_ts = reflect.TypeOf([]*big.Int(nil))
-var int_ts = reflect.TypeOf([]int(nil))
-var int8_ts = reflect.TypeOf([]int8(nil))
-var int16_ts = reflect.TypeOf([]int16(nil))
-var int32_ts = reflect.TypeOf([]int32(nil))
-var int64_ts = reflect.TypeOf([]int64(nil))
-var big_ts = reflect.TypeOf([]*big.Int(nil))
+ int_ts = reflect.TypeOf([]int(nil))
+ int8_ts = reflect.TypeOf([]int8(nil))
+ int16_ts = reflect.TypeOf([]int16(nil))
+ int32_ts = reflect.TypeOf([]int32(nil))
+ int64_ts = reflect.TypeOf([]int64(nil))
+ big_ts = reflect.TypeOf([]*big.Int(nil))
+)
// U256 will ensure unsigned 256bit on big nums
func U256(n *big.Int) []byte {
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 6fb2950ba..c08b744f7 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -163,6 +163,13 @@ func (t Type) String() (out string) {
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
// criteria
// * 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 {
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:
+ // 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 {
return nil, fmt.Errorf("%v out of bound. %d for %d", value.Kind(), value.Len(), t.Size)
}