Merge pull request #2210 from obscuren/abi-typed-array
accounts/abi: support for typed array
This commit is contained in:
commit
4f28c5b69d
@ -165,7 +165,14 @@ func (abi ABI) Call(executer Executer, v interface{}, name string, args ...inter
|
|||||||
return abi.unmarshal(v, name, executer(callData))
|
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
|
// unmarshal output in v according to the abi specification
|
||||||
func (abi ABI) unmarshal(v interface{}, name string, output []byte) error {
|
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)
|
field := typ.Field(j)
|
||||||
// TODO read tags: `abi:"fieldName"`
|
// TODO read tags: `abi:"fieldName"`
|
||||||
if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
|
if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
|
||||||
if field.Type.AssignableTo(reflectValue.Type()) {
|
if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil {
|
||||||
value.Field(j).Set(reflectValue)
|
return err
|
||||||
break
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("abi: cannot unmarshal %v in to %v", field.Type, reflectValue.Type())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case reflect.Slice:
|
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)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
reflectValue := reflect.ValueOf(marshalledValue)
|
if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
|
||||||
if typ.AssignableTo(reflectValue.Type()) {
|
return err
|
||||||
value.Set(reflectValue)
|
|
||||||
} else {
|
|
||||||
return fmt.Errorf("abi: cannot unmarshal %v in to %v", reflectValue.Type(), value.Type())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
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 {
|
func (abi *ABI) UnmarshalJSON(data []byte) error {
|
||||||
var fields []struct {
|
var fields []struct {
|
||||||
Type string
|
Type string
|
||||||
|
@ -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) {
|
func TestDefaultFunctionParsing(t *testing.T) {
|
||||||
const definition = `[{ "name" : "balance" }]`
|
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) {
|
func TestUnmarshal(t *testing.T) {
|
||||||
const definition = `[
|
const definition = `[
|
||||||
{ "name" : "int", "const" : false, "outputs": [ { "type": "uint256" } ] },
|
{ "name" : "int", "const" : false, "outputs": [ { "type": "uint256" } ] },
|
||||||
{ "name" : "bool", "const" : false, "outputs": [ { "type": "bool" } ] },
|
{ "name" : "bool", "const" : false, "outputs": [ { "type": "bool" } ] },
|
||||||
{ "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] },
|
{ "name" : "bytes", "const" : false, "outputs": [ { "type": "bytes" } ] },
|
||||||
|
{ "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" : "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" } ] }]`
|
||||||
|
|
||||||
@ -655,6 +684,21 @@ func TestUnmarshal(t *testing.T) {
|
|||||||
t.Errorf("expected %x got %x", bytesOut, Bytes)
|
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
|
// marshal error
|
||||||
buff.Reset()
|
buff.Reset()
|
||||||
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
|
||||||
|
Loading…
Reference in New Issue
Block a user