aboutsummaryrefslogtreecommitdiffstats
path: root/accounts/abi
diff options
context:
space:
mode:
authorJeffrey Wilcke <geffobscura@gmail.com>2016-02-13 08:42:23 +0800
committerJeffrey Wilcke <geffobscura@gmail.com>2016-02-15 01:56:56 +0800
commit6fe917ecb86fddbf6132e7cedd1a0455796451f4 (patch)
tree8c1750ad2fcce3a9fe8e6f8fa1c82b4a24027ba5 /accounts/abi
parentcb85923828d5ea45b53189b3b29ab014d9601f09 (diff)
downloaddexon-6fe917ecb86fddbf6132e7cedd1a0455796451f4.tar
dexon-6fe917ecb86fddbf6132e7cedd1a0455796451f4.tar.gz
dexon-6fe917ecb86fddbf6132e7cedd1a0455796451f4.tar.bz2
dexon-6fe917ecb86fddbf6132e7cedd1a0455796451f4.tar.lz
dexon-6fe917ecb86fddbf6132e7cedd1a0455796451f4.tar.xz
dexon-6fe917ecb86fddbf6132e7cedd1a0455796451f4.tar.zst
dexon-6fe917ecb86fddbf6132e7cedd1a0455796451f4.zip
accounts/abi: support for typed array
Added support for fixed size and arbitrary length byte arrays to be marshallable in fixed size (typed) byte slices.
Diffstat (limited to 'accounts/abi')
-rw-r--r--accounts/abi/abi.go51
-rw-r--r--accounts/abi/abi_test.go106
2 files changed, 114 insertions, 43 deletions
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 2dc8039f5..324d3c76f 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -165,7 +165,14 @@ func (abi ABI) Call(executer Executer, v interface{}, name string, args ...inter
return abi.unmarshal(v, name, executer(callData))
}
-var interSlice = reflect.TypeOf([]interface{}{})
+// these variable are used to determine certain types during type assertion for
+// assignment.
+var (
+ r_interSlice = reflect.TypeOf([]interface{}{})
+ r_hash = reflect.TypeOf(common.Hash{})
+ r_bytes = reflect.TypeOf([]byte{})
+ r_byte = reflect.TypeOf(byte(0))
+)
// unmarshal output in v according to the abi specification
func (abi ABI) unmarshal(v interface{}, name string, output []byte) error {
@@ -194,17 +201,14 @@ func (abi ABI) unmarshal(v interface{}, name string, output []byte) error {
field := typ.Field(j)
// TODO read tags: `abi:"fieldName"`
if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
- if field.Type.AssignableTo(reflectValue.Type()) {
- value.Field(j).Set(reflectValue)
- break
- } else {
- return fmt.Errorf("abi: cannot unmarshal %v in to %v", field.Type, reflectValue.Type())
+ if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil {
+ return err
}
}
}
}
case reflect.Slice:
- if !value.Type().AssignableTo(interSlice) {
+ if !value.Type().AssignableTo(r_interSlice) {
return fmt.Errorf("abi: cannot marshal tuple in to slice %T (only []interface{} is supported)", v)
}
@@ -228,17 +232,40 @@ func (abi ABI) unmarshal(v interface{}, name string, output []byte) error {
if err != nil {
return err
}
- reflectValue := reflect.ValueOf(marshalledValue)
- if typ.AssignableTo(reflectValue.Type()) {
- value.Set(reflectValue)
- } else {
- return fmt.Errorf("abi: cannot unmarshal %v in to %v", reflectValue.Type(), value.Type())
+ if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
+ return err
}
}
return nil
}
+// set attempts to assign src to dst by either setting, copying or otherwise.
+//
+// set is a bit more lenient when it comes to assignment and doesn't force an as
+// strict ruleset as bare `reflect` does.
+func set(dst, src reflect.Value, output Argument) error {
+ dstType := dst.Type()
+ srcType := src.Type()
+
+ switch {
+ case dstType.AssignableTo(src.Type()):
+ dst.Set(src)
+ case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice:
+ if !dstType.Elem().AssignableTo(r_byte) {
+ return fmt.Errorf("abi: cannot unmarshal %v in to array of elem %v", src.Type(), dstType.Elem())
+ }
+
+ if dst.Len() < output.Type.Size {
+ return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.Size, dst.Len())
+ }
+ reflect.Copy(dst, src)
+ default:
+ return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
+ }
+ return nil
+}
+
func (abi *ABI) UnmarshalJSON(data []byte) error {
var fields []struct {
Type string
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index bb0143d21..c6a8705cd 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -394,37 +394,6 @@ func TestBytes(t *testing.T) {
}
}
-/*
-func TestReturn(t *testing.T) {
- const definition = `[
- { "type" : "function", "name" : "balance", "const" : true, "inputs" : [], "outputs" : [ { "name": "", "type": "hash" } ] },
- { "type" : "function", "name" : "name", "const" : true, "inputs" : [], "outputs" : [ { "name": "", "type": "address" } ] }]`
-
- abi, err := JSON(strings.NewReader(definition))
- if err != nil {
- t.Fatal(err)
- }
-
- r := abi.Call(func([]byte) []byte {
- t := make([]byte, 32)
- t[0] = 1
- return t
- }, "balance")
- if _, ok := r.(common.Hash); !ok {
- t.Errorf("expected type common.Hash, got %T", r)
- }
-
- r = abi.Call(func([]byte) []byte {
- t := make([]byte, 32)
- t[0] = 1
- return t
- }, "name")
- if _, ok := r.(common.Address); !ok {
- t.Errorf("expected type common.Address, got %T", r)
- }
-}
-*/
-
func TestDefaultFunctionParsing(t *testing.T) {
const definition = `[{ "name" : "balance" }]`
@@ -550,11 +519,71 @@ func TestMultiReturnWithSlice(t *testing.T) {
}
}
+func TestMarshalArrays(t *testing.T) {
+ const definition = `[
+ { "name" : "bytes32", "const" : false, "outputs": [ { "type": "bytes32" } ] },
+ { "name" : "bytes10", "const" : false, "outputs": [ { "type": "bytes10" } ] }
+ ]`
+
+ abi, err := JSON(strings.NewReader(definition))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ output := common.LeftPadBytes([]byte{1}, 32)
+
+ var bytes10 [10]byte
+ err = abi.unmarshal(&bytes10, "bytes32", output)
+ if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
+ t.Error("expected error or bytes32 not be assignable to bytes10:", err)
+ }
+
+ var bytes32 [32]byte
+ err = abi.unmarshal(&bytes32, "bytes32", output)
+ if err != nil {
+ t.Error("didn't expect error:", err)
+ }
+ if !bytes.Equal(bytes32[:], output) {
+ t.Error("expected bytes32[31] to be 1 got", bytes32[31])
+ }
+
+ type (
+ B10 [10]byte
+ B32 [32]byte
+ )
+
+ var b10 B10
+ err = abi.unmarshal(&b10, "bytes32", output)
+ if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
+ t.Error("expected error or bytes32 not be assignable to bytes10:", err)
+ }
+
+ var b32 B32
+ err = abi.unmarshal(&b32, "bytes32", output)
+ if err != nil {
+ t.Error("didn't expect error:", err)
+ }
+ if !bytes.Equal(b32[:], output) {
+ t.Error("expected bytes32[31] to be 1 got", bytes32[31])
+ }
+
+ output[10] = 1
+ var shortAssignLong [32]byte
+ err = abi.unmarshal(&shortAssignLong, "bytes10", output)
+ if err != nil {
+ t.Error("didn't expect error:", err)
+ }
+ if !bytes.Equal(output, shortAssignLong[:]) {
+ t.Errorf("expected %x to be %x", shortAssignLong, output)
+ }
+}
+
func TestUnmarshal(t *testing.T) {
const definition = `[
{ "name" : "int", "const" : false, "outputs": [ { "type": "uint256" } ] },
{ "name" : "bool", "const" : false, "outputs": [ { "type": "bool" } ] },
{ "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] },
+ { "name" : "fixed", "const" : false, "outputs": [ { "type": "bytes32" } ] },
{ "name" : "multi", "const" : false, "outputs": [ { "type": "bytes" }, { "type": "bytes" } ] },
{ "name" : "mixedBytes", "const" : true, "outputs": [ { "name": "a", "type": "bytes" }, { "name": "b", "type": "bytes32" } ] }]`
@@ -655,6 +684,21 @@ func TestUnmarshal(t *testing.T) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
+ // marshal dynamic bytes length 5
+ buff.Reset()
+ buff.Write(common.RightPadBytes([]byte("hello"), 32))
+
+ var hash common.Hash
+ err = abi.unmarshal(&hash, "fixed", buff.Bytes())
+ if err != nil {
+ t.Error(err)
+ }
+
+ helloHash := common.BytesToHash(common.RightPadBytes([]byte("hello"), 32))
+ if hash != helloHash {
+ t.Errorf("Expected %x to equal %x", hash, helloHash)
+ }
+
// marshal error
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))