From 1f72952f043e9df3df71eb27a6ed7c6bef93a3f2 Mon Sep 17 00:00:00 2001 From: Jeffrey Wilcke Date: Wed, 28 Oct 2015 22:57:21 +0100 Subject: [PATCH] accounts/abi: ABI fixes & added types Changed field `input` to new `inputs`. Addad Hash and Address as input types. Added bytes[N] and N validation --- accounts/abi/abi.go | 12 +++---- accounts/abi/abi_test.go | 75 +++++++++++++++++++++++++++++++++------- accounts/abi/type.go | 14 ++++++-- 3 files changed, 80 insertions(+), 21 deletions(-) diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go index de3128902b..3f05bfe2d4 100644 --- a/accounts/abi/abi.go +++ b/accounts/abi/abi.go @@ -36,7 +36,7 @@ import ( type Method struct { Name string Const bool - Input []Argument + Inputs []Argument Return Type // not yet implemented } @@ -49,9 +49,9 @@ type Method struct { // Please note that "int" is substitute for its canonical representation "int256" func (m Method) String() (out string) { out += m.Name - types := make([]string, len(m.Input)) + types := make([]string, len(m.Inputs)) i := 0 - for _, input := range m.Input { + for _, input := range m.Inputs { types[i] = input.Type.String() i++ } @@ -104,7 +104,7 @@ func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) { var ret []byte for i, a := range args { - input := method.Input[i] + input := method.Inputs[i] packed, err := input.Type.pack(a) if err != nil { @@ -129,8 +129,8 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) { } // start with argument count match - if len(args) != len(method.Input) { - return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Input)) + if len(args) != len(method.Inputs) { + return nil, fmt.Errorf("argument count mismatch: %d for %d", len(args), len(method.Inputs)) } arguments, err := abi.pack(name, args...) diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go index 7706de05d9..96dd3ee62b 100644 --- a/accounts/abi/abi_test.go +++ b/accounts/abi/abi_test.go @@ -18,35 +18,38 @@ package abi import ( "bytes" + "fmt" + "log" "math/big" "reflect" "strings" "testing" + "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/crypto" ) const jsondata = ` [ { "name" : "balance", "const" : true }, - { "name" : "send", "const" : false, "input" : [ { "name" : "amount", "type" : "uint256" } ] } + { "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] } ]` const jsondata2 = ` [ { "name" : "balance", "const" : true }, - { "name" : "send", "const" : false, "input" : [ { "name" : "amount", "type" : "uint256" } ] }, - { "name" : "test", "const" : false, "input" : [ { "name" : "number", "type" : "uint32" } ] }, - { "name" : "string", "const" : false, "input" : [ { "name" : "input", "type" : "string" } ] }, - { "name" : "bool", "const" : false, "input" : [ { "name" : "input", "type" : "bool" } ] }, - { "name" : "address", "const" : false, "input" : [ { "name" : "input", "type" : "address" } ] }, - { "name" : "string32", "const" : false, "input" : [ { "name" : "input", "type" : "string32" } ] }, - { "name" : "uint64[2]", "const" : false, "input" : [ { "name" : "input", "type" : "uint64[2]" } ] }, - { "name" : "uint64[]", "const" : false, "input" : [ { "name" : "input", "type" : "uint64[]" } ] }, - { "name" : "foo", "const" : false, "input" : [ { "name" : "input", "type" : "uint32" } ] }, - { "name" : "bar", "const" : false, "input" : [ { "name" : "input", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] }, - { "name" : "slice", "const" : false, "input" : [ { "name" : "input", "type" : "uint32[2]" } ] }, - { "name" : "slice256", "const" : false, "input" : [ { "name" : "input", "type" : "uint256[2]" } ] } + { "name" : "send", "const" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] }, + { "name" : "test", "const" : false, "inputs" : [ { "name" : "number", "type" : "uint32" } ] }, + { "name" : "string", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string" } ] }, + { "name" : "bool", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "bool" } ] }, + { "name" : "address", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "address" } ] }, + { "name" : "string32", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "string32" } ] }, + { "name" : "uint64[2]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[2]" } ] }, + { "name" : "uint64[]", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint64[]" } ] }, + { "name" : "foo", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" } ] }, + { "name" : "bar", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32" }, { "name" : "string", "type" : "uint16" } ] }, + { "name" : "slice", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] }, + { "name" : "slice256", "const" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] } ]` func TestType(t *testing.T) { @@ -344,3 +347,49 @@ func TestPackSliceBig(t *testing.T) { t.Errorf("expected %x got %x", sig, packed) } } + +func ExampleJSON() { + const definition = `[{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"isBar","outputs":[{"name":"","type":"bool"}],"type":"function"}]` + + abi, err := JSON(strings.NewReader(definition)) + if err != nil { + log.Fatalln(err) + } + out, err := abi.Pack("isBar", common.HexToAddress("01")) + if err != nil { + log.Fatalln(err) + } + + fmt.Printf("%x\n", out) + // Output: + // 1f2c40920000000000000000000000000000000000000000000000000000000000000001 +} + +func TestBytes(t *testing.T) { + const definition = `[ + { "name" : "balance", "const" : true, "inputs" : [ { "name" : "address", "type" : "bytes20" } ] }, + { "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") + } +} diff --git a/accounts/abi/type.go b/accounts/abi/type.go index b16822d3ba..16d7491e7c 100644 --- a/accounts/abi/type.go +++ b/accounts/abi/type.go @@ -43,7 +43,7 @@ type Type struct { stringKind string // holds the unparsed string for deriving signatures } -// New type returns a fully parsed Type given by the input string or an error if it can't be parsed. +// NewType returns a fully parsed Type given by the input string or an error if it can't be parsed. // // Strings can be in the format of: // @@ -130,6 +130,10 @@ func NewType(t string) (typ Type, err error) { if vsize > 0 { typ.Size = 32 } + case "bytes": + typ.Kind = reflect.Slice + typ.Type = byte_ts + typ.Size = vsize default: return Type{}, fmt.Errorf("unsupported arg type: %s", t) } @@ -200,7 +204,13 @@ func (t Type) pack(v interface{}) ([]byte, error) { } else { return common.LeftPadBytes(common.Big0.Bytes(), 32), nil } + case reflect.Array: + if v, ok := value.Interface().(common.Address); ok { + return t.pack(v[:]) + } else if v, ok := value.Interface().(common.Hash); ok { + return t.pack(v[:]) + } } - panic("unreached") + return nil, fmt.Errorf("ABI: bad input given %T", value.Kind()) }