Răsfoiți Sursa

Implemented contract ABI

obscuren 10 ani în urmă
părinte
comite
aa5b29e2f2

+ 155 - 0
accounts/abi/abi.go

@@ -0,0 +1,155 @@
+package abi
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"strings"
+
+	"github.com/ethereum/go-ethereum/crypto"
+)
+
+// Callable method given a `Name` and whether the method is a constant.
+// If the method is `Const` no transaction needs to be created for this
+// particular Method call. It can easily be simulated using a local VM.
+// For example a `Balance()` method only needs to retrieve something
+// from the storage and therefor requires no Tx to be send to the
+// network. A method such as `Transact` does require a Tx and thus will
+// be flagged `true`.
+// Input specifies the required input parameters for this gives method.
+type Method struct {
+	Name   string
+	Const  bool
+	Input  []Argument
+	Return Type // not yet implemented
+}
+
+// Returns the methods string signature according to the ABI spec.
+//
+// Example
+//
+//     function foo(uint32 a, int b)    =    "foo(uint32,int256)"
+//
+// 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))
+	i := 0
+	for _, input := range m.Input {
+		types[i] = input.Type.String()
+		i++
+	}
+	out += "(" + strings.Join(types, ",") + ")"
+
+	return
+}
+
+func (m Method) Id() []byte {
+	return crypto.Sha3([]byte(m.String()))[:4]
+}
+
+// Argument holds the name of the argument and the corresponding type.
+// Types are used when packing and testing arguments.
+type Argument struct {
+	Name string
+	Type Type
+}
+
+func (a *Argument) UnmarshalJSON(data []byte) error {
+	var extarg struct {
+		Name string
+		Type string
+	}
+	err := json.Unmarshal(data, &extarg)
+	if err != nil {
+		return fmt.Errorf("argument json err: %v", err)
+	}
+
+	a.Type, err = NewType(extarg.Type)
+	if err != nil {
+		return err
+	}
+	a.Name = extarg.Name
+
+	return nil
+}
+
+// The ABI holds information about a contract's context and available
+// invokable methods. It will allow you to type check function calls and
+// packs data accordingly.
+type ABI struct {
+	Methods map[string]Method
+}
+
+// tests, tests whether the given input would result in a successful
+// call. Checks argument list count and matches input to `input`.
+func (abi ABI) pack(name string, args ...interface{}) ([]byte, error) {
+	method := abi.Methods[name]
+
+	var ret []byte
+	for i, a := range args {
+		input := method.Input[i]
+
+		packed, err := input.Type.pack(a)
+		if err != nil {
+			return nil, fmt.Errorf("`%s` %v", name, err)
+		}
+		ret = append(ret, packed...)
+
+	}
+
+	return ret, nil
+}
+
+// Pack the given method name to conform the ABI. Method call's data
+// will consist of method_id, args0, arg1, ... argN. Method id consists
+// of 4 bytes and arguments are all 32 bytes.
+// Method ids are created from the first 4 bytes of the hash of the
+// methods string signature. (signature = baz(uint32,string32))
+func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
+	method, exist := abi.Methods[name]
+	if !exist {
+		return nil, fmt.Errorf("method '%s' not found", name)
+	}
+
+	// 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))
+	}
+
+	arguments, err := abi.pack(name, args...)
+	if err != nil {
+		return nil, err
+	}
+
+	// Set function id
+	packed := abi.Methods[name].Id()
+	packed = append(packed, arguments...)
+
+	return packed, nil
+}
+
+func (abi *ABI) UnmarshalJSON(data []byte) error {
+	var methods []Method
+	if err := json.Unmarshal(data, &methods); err != nil {
+		return err
+	}
+
+	abi.Methods = make(map[string]Method)
+	for _, method := range methods {
+		abi.Methods[method.Name] = method
+	}
+
+	return nil
+}
+
+func JSON(reader io.Reader) (ABI, error) {
+	dec := json.NewDecoder(reader)
+
+	var abi ABI
+	if err := dec.Decode(&abi); err != nil {
+		return ABI{}, err
+	}
+
+	return abi, nil
+}

+ 330 - 0
accounts/abi/abi_test.go

@@ -0,0 +1,330 @@
+package abi
+
+import (
+	"bytes"
+	"math/big"
+	"reflect"
+	"strings"
+	"testing"
+
+	"github.com/ethereum/go-ethereum/crypto"
+)
+
+const jsondata = `
+[
+	{ "name" : "balance", "const" : true },
+	{ "name" : "send", "const" : false, "input" : [ { "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]" } ] }
+]`
+
+func TestType(t *testing.T) {
+	typ, err := NewType("uint32")
+	if err != nil {
+		t.Error(err)
+	}
+	if typ.Kind != reflect.Ptr {
+		t.Error("expected uint32 to have kind Ptr")
+	}
+
+	typ, err = NewType("uint32[]")
+	if err != nil {
+		t.Error(err)
+	}
+	if typ.Kind != reflect.Slice {
+		t.Error("expected uint32[] to have type slice")
+	}
+	if typ.Type != ubig_ts {
+		t.Error("expcted uith32[] to have type uint64")
+	}
+
+	typ, err = NewType("uint32[2]")
+	if err != nil {
+		t.Error(err)
+	}
+	if typ.Kind != reflect.Slice {
+		t.Error("expected uint32[2] to have kind slice")
+	}
+	if typ.Type != ubig_ts {
+		t.Error("expcted uith32[2] to have type uint64")
+	}
+	if typ.Size != 2 {
+		t.Error("expected uint32[2] to have a size of 2")
+	}
+}
+
+func TestReader(t *testing.T) {
+	Uint256, _ := NewType("uint256")
+	exp := ABI{
+		Methods: map[string]Method{
+			"balance": Method{
+				"balance", true, nil, Type{},
+			},
+			"send": Method{
+				"send", false, []Argument{
+					Argument{"amount", Uint256},
+				}, Type{},
+			},
+		},
+	}
+
+	abi, err := JSON(strings.NewReader(jsondata))
+	if err != nil {
+		t.Error(err)
+	}
+
+	// deep equal fails for some reason
+	t.Skip()
+	if !reflect.DeepEqual(abi, exp) {
+		t.Errorf("\nabi: %v\ndoes not match exp: %v", abi, exp)
+	}
+}
+
+func TestTestNumbers(t *testing.T) {
+	abi, err := JSON(strings.NewReader(jsondata2))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	if _, err := abi.Pack("balance"); err != nil {
+		t.Error(err)
+	}
+
+	if _, err := abi.Pack("balance", 1); err == nil {
+		t.Error("expected error for balance(1)")
+	}
+
+	if _, err := abi.Pack("doesntexist", nil); err == nil {
+		t.Errorf("doesntexist shouldn't exist")
+	}
+
+	if _, err := abi.Pack("doesntexist", 1); err == nil {
+		t.Errorf("doesntexist(1) shouldn't exist")
+	}
+
+	if _, err := abi.Pack("send", big.NewInt(1000)); err != nil {
+		t.Error(err)
+	}
+
+	i := new(int)
+	*i = 1000
+	if _, err := abi.Pack("send", i); err == nil {
+		t.Errorf("expected send( ptr ) to throw, requires *big.Int instead of *int")
+	}
+
+	if _, err := abi.Pack("send", 1000); err != nil {
+		t.Error("expected send(1000) to cast to big")
+	}
+
+	if _, err := abi.Pack("test", uint32(1000)); err != nil {
+		t.Error(err)
+	}
+}
+
+func TestTestString(t *testing.T) {
+	abi, err := JSON(strings.NewReader(jsondata2))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	if _, err := abi.Pack("string", "hello world"); err != nil {
+		t.Error(err)
+	}
+
+	str10 := string(make([]byte, 10))
+	if _, err := abi.Pack("string32", str10); err != nil {
+		t.Error(err)
+	}
+
+	str32 := string(make([]byte, 32))
+	if _, err := abi.Pack("string32", str32); err != nil {
+		t.Error(err)
+	}
+
+	str33 := string(make([]byte, 33))
+	if _, err := abi.Pack("string32", str33); err == nil {
+		t.Error("expected str33 to throw out of bound error")
+	}
+}
+
+func TestTestBool(t *testing.T) {
+	abi, err := JSON(strings.NewReader(jsondata2))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	if _, err := abi.Pack("bool", true); err != nil {
+		t.Error(err)
+	}
+}
+
+func TestTestSlice(t *testing.T) {
+	abi, err := JSON(strings.NewReader(jsondata2))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	addr := make([]byte, 20)
+	if _, err := abi.Pack("address", addr); err != nil {
+		t.Error(err)
+	}
+
+	addr = make([]byte, 21)
+	if _, err := abi.Pack("address", addr); err == nil {
+		t.Error("expected address of 21 width to throw")
+	}
+
+	slice := make([]byte, 2)
+	if _, err := abi.Pack("uint64[2]", slice); err != nil {
+		t.Error(err)
+	}
+
+	if _, err := abi.Pack("uint64[]", slice); err != nil {
+		t.Error(err)
+	}
+}
+
+func TestTestAddress(t *testing.T) {
+	abi, err := JSON(strings.NewReader(jsondata2))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	addr := make([]byte, 20)
+	if _, err := abi.Pack("address", addr); err != nil {
+		t.Error(err)
+	}
+}
+
+func TestMethodSignature(t *testing.T) {
+	String, _ := NewType("string")
+	String32, _ := NewType("string32")
+	m := Method{"foo", false, []Argument{Argument{"bar", String32}, Argument{"baz", String}}, Type{}}
+	exp := "foo(string32,string)"
+	if m.String() != exp {
+		t.Error("signature mismatch", exp, "!=", m.String())
+	}
+
+	idexp := crypto.Sha3([]byte(exp))[:4]
+	if !bytes.Equal(m.Id(), idexp) {
+		t.Errorf("expected ids to match %x != %x", m.Id(), idexp)
+	}
+
+	uintt, _ := NewType("uint")
+	m = Method{"foo", false, []Argument{Argument{"bar", uintt}}, Type{}}
+	exp = "foo(uint256)"
+	if m.String() != exp {
+		t.Error("signature mismatch", exp, "!=", m.String())
+	}
+}
+
+func TestPack(t *testing.T) {
+	abi, err := JSON(strings.NewReader(jsondata2))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	sig := crypto.Sha3([]byte("foo(uint32)"))[:4]
+	sig = append(sig, make([]byte, 32)...)
+	sig[35] = 10
+
+	packed, err := abi.Pack("foo", uint32(10))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	if !bytes.Equal(packed, sig) {
+		t.Errorf("expected %x got %x", sig, packed)
+	}
+}
+
+func TestMultiPack(t *testing.T) {
+	abi, err := JSON(strings.NewReader(jsondata2))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	sig := crypto.Sha3([]byte("bar(uint32,uint16)"))[:4]
+	sig = append(sig, make([]byte, 64)...)
+	sig[35] = 10
+	sig[67] = 11
+
+	packed, err := abi.Pack("bar", uint32(10), uint16(11))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	if !bytes.Equal(packed, sig) {
+		t.Errorf("expected %x got %x", sig, packed)
+	}
+}
+
+func TestPackSlice(t *testing.T) {
+	abi, err := JSON(strings.NewReader(jsondata2))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	sig := crypto.Sha3([]byte("slice(uint32[2])"))[:4]
+	sig = append(sig, make([]byte, 64)...)
+	sig[35] = 1
+	sig[67] = 2
+
+	packed, err := abi.Pack("slice", []uint32{1, 2})
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	if !bytes.Equal(packed, sig) {
+		t.Errorf("expected %x got %x", sig, packed)
+	}
+}
+
+func TestPackSliceBig(t *testing.T) {
+	abi, err := JSON(strings.NewReader(jsondata2))
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	sig := crypto.Sha3([]byte("slice256(uint256[2])"))[:4]
+	sig = append(sig, make([]byte, 64)...)
+	sig[35] = 1
+	sig[67] = 2
+
+	packed, err := abi.Pack("slice256", []*big.Int{big.NewInt(1), big.NewInt(2)})
+	if err != nil {
+		t.Error(err)
+		t.FailNow()
+	}
+
+	if !bytes.Equal(packed, sig) {
+		t.Errorf("expected %x got %x", sig, packed)
+	}
+}

+ 10 - 0
accounts/abi/doc.go

@@ -0,0 +1,10 @@
+// Package abi implements the Ethereum ABI (Application Binary
+// Interface).
+//
+// The Ethereum ABI is strongly typed, known at compile time
+// and static. This ABI will handle basic type casting; unsigned
+// to signed and visa versa. It does not handle slice casting such
+// as unsigned slice to signed slice. Bit size type casting is also
+// handled. ints with a bit size of 32 will be properly cast to int256,
+// etc.
+package abi

+ 106 - 0
accounts/abi/numbers.go

@@ -0,0 +1,106 @@
+package abi
+
+import (
+	"math/big"
+	"reflect"
+
+	"github.com/ethereum/go-ethereum/ethutil"
+)
+
+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 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))
+
+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))
+
+// U256 will ensure unsigned 256bit on big nums
+func U256(n *big.Int) []byte {
+	return ethutil.LeftPadBytes(ethutil.U256(n).Bytes(), 32)
+}
+
+func S256(n *big.Int) []byte {
+	sint := ethutil.S256(n)
+	ret := ethutil.LeftPadBytes(sint.Bytes(), 32)
+	if sint.Cmp(ethutil.Big0) < 0 {
+		for i, b := range ret {
+			if b == 0 {
+				ret[i] = 1
+				continue
+			}
+			break
+		}
+	}
+
+	return ret
+}
+
+// S256 will ensure signed 256bit on big nums
+func U2U256(n uint64) []byte {
+	return U256(big.NewInt(int64(n)))
+}
+
+func S2S256(n int64) []byte {
+	return S256(big.NewInt(n))
+}
+
+// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
+func packNum(value reflect.Value, to byte) []byte {
+	switch kind := value.Kind(); kind {
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		if to == UintTy {
+			return U2U256(value.Uint())
+		} else {
+			return S2S256(int64(value.Uint()))
+		}
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		if to == UintTy {
+			return U2U256(uint64(value.Int()))
+		} else {
+			return S2S256(value.Int())
+		}
+	case reflect.Ptr:
+		// This only takes care of packing and casting. No type checking is done here. It should be done prior to using this function.
+		if to == UintTy {
+			return U256(value.Interface().(*big.Int))
+		} else {
+			return S256(value.Interface().(*big.Int))
+		}
+
+	}
+
+	return nil
+}
+
+// checks whether the given reflect value is signed. This also works for slices with a number type
+func isSigned(v reflect.Value) bool {
+	switch v.Type() {
+	case ubig_ts, big_ts, big_t, ubig_t:
+		return true
+	case int_ts, int8_ts, int16_ts, int32_ts, int64_ts, int_t, int8_t, int16_t, int32_t, int64_t:
+		return true
+	}
+	return false
+}

+ 72 - 0
accounts/abi/numbers_test.go

@@ -0,0 +1,72 @@
+package abi
+
+import (
+	"bytes"
+	"math/big"
+	"reflect"
+	"testing"
+)
+
+func TestNumberTypes(t *testing.T) {
+	ubytes := make([]byte, 32)
+	ubytes[31] = 1
+	sbytesmin := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+
+	unsigned := U256(big.NewInt(1))
+	if !bytes.Equal(unsigned, ubytes) {
+		t.Error("expected %x got %x", ubytes, unsigned)
+	}
+
+	signed := S256(big.NewInt(1))
+	if !bytes.Equal(signed, ubytes) {
+		t.Error("expected %x got %x", ubytes, unsigned)
+	}
+
+	signed = S256(big.NewInt(-1))
+	if !bytes.Equal(signed, sbytesmin) {
+		t.Error("expected %x got %x", ubytes, unsigned)
+	}
+}
+
+func TestPackNumber(t *testing.T) {
+	ubytes := make([]byte, 32)
+	ubytes[31] = 1
+	sbytesmin := []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+	maxunsigned := []byte{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255}
+
+	packed := packNum(reflect.ValueOf(1), IntTy)
+	if !bytes.Equal(packed, ubytes) {
+		t.Errorf("expected %x got %x", ubytes, packed)
+	}
+	packed = packNum(reflect.ValueOf(-1), IntTy)
+	if !bytes.Equal(packed, sbytesmin) {
+		t.Errorf("expected %x got %x", ubytes, packed)
+	}
+	packed = packNum(reflect.ValueOf(1), UintTy)
+	if !bytes.Equal(packed, ubytes) {
+		t.Errorf("expected %x got %x", ubytes, packed)
+	}
+	packed = packNum(reflect.ValueOf(-1), UintTy)
+	if !bytes.Equal(packed, maxunsigned) {
+		t.Errorf("expected %x got %x", maxunsigned, packed)
+	}
+
+	packed = packNum(reflect.ValueOf("string"), UintTy)
+	if packed != nil {
+		t.Errorf("expected 'string' to pack to nil. got %x instead", packed)
+	}
+}
+
+func TestSigned(t *testing.T) {
+	if isSigned(reflect.ValueOf(uint(10))) {
+		t.Error()
+	}
+
+	if !isSigned(reflect.ValueOf(int(10))) {
+		t.Error()
+	}
+
+	if !isSigned(reflect.ValueOf(big.NewInt(10))) {
+		t.Error()
+	}
+}

+ 106 - 0
accounts/abi/profile.cov

@@ -0,0 +1,106 @@
+mode: set
+github.com/ethereum/go-ethereum/abi/abi.go:27.39,31.32 4 1
+github.com/ethereum/go-ethereum/abi/abi.go:35.2,37.8 2 1
+github.com/ethereum/go-ethereum/abi/abi.go:31.32,34.3 2 1
+github.com/ethereum/go-ethereum/abi/abi.go:40.29,42.2 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:51.53,57.16 3 1
+github.com/ethereum/go-ethereum/abi/abi.go:61.2,62.16 2 1
+github.com/ethereum/go-ethereum/abi/abi.go:65.2,67.12 2 1
+github.com/ethereum/go-ethereum/abi/abi.go:57.16,59.3 1 0
+github.com/ethereum/go-ethereum/abi/abi.go:62.16,64.3 1 0
+github.com/ethereum/go-ethereum/abi/abi.go:79.71,83.25 3 1
+github.com/ethereum/go-ethereum/abi/abi.go:94.2,94.17 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:83.25,87.17 3 1
+github.com/ethereum/go-ethereum/abi/abi.go:90.3,90.31 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:87.17,89.4 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:102.71,104.12 2 1
+github.com/ethereum/go-ethereum/abi/abi.go:109.2,109.36 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:113.2,114.16 2 1
+github.com/ethereum/go-ethereum/abi/abi.go:119.2,122.20 3 1
+github.com/ethereum/go-ethereum/abi/abi.go:104.12,106.3 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:109.36,111.3 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:114.16,116.3 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:125.50,127.55 2 1
+github.com/ethereum/go-ethereum/abi/abi.go:131.2,132.33 2 1
+github.com/ethereum/go-ethereum/abi/abi.go:136.2,136.12 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:127.55,129.3 1 0
+github.com/ethereum/go-ethereum/abi/abi.go:132.33,134.3 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:139.42,143.41 3 1
+github.com/ethereum/go-ethereum/abi/abi.go:147.2,147.17 1 1
+github.com/ethereum/go-ethereum/abi/abi.go:143.41,145.3 1 0
+github.com/ethereum/go-ethereum/abi/numbers.go:39.30,41.2 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:43.30,46.32 3 1
+github.com/ethereum/go-ethereum/abi/numbers.go:56.2,56.12 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:46.32,47.25 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:47.25,48.14 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:52.4,52.9 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:48.14,50.13 2 1
+github.com/ethereum/go-ethereum/abi/numbers.go:59.30,61.2 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:63.29,65.2 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:67.51,68.36 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:91.2,91.12 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:69.2,70.19 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:75.2,76.19 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:81.2,83.19 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:70.19,72.4 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:72.4,74.4 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:76.19,78.4 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:78.4,80.4 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:83.19,85.4 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:85.4,87.4 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:94.37,95.18 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:101.2,101.14 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:96.2,97.14 1 1
+github.com/ethereum/go-ethereum/abi/numbers.go:98.2,99.14 1 1
+github.com/ethereum/go-ethereum/abi/type.go:32.46,35.16 2 1
+github.com/ethereum/go-ethereum/abi/type.go:38.2,43.9 3 1
+github.com/ethereum/go-ethereum/abi/type.go:55.2,56.16 2 1
+github.com/ethereum/go-ethereum/abi/type.go:60.2,64.55 4 1
+github.com/ethereum/go-ethereum/abi/type.go:69.2,69.13 1 1
+github.com/ethereum/go-ethereum/abi/type.go:111.2,113.8 2 1
+github.com/ethereum/go-ethereum/abi/type.go:35.16,37.3 1 0
+github.com/ethereum/go-ethereum/abi/type.go:44.2,47.17 2 1
+github.com/ethereum/go-ethereum/abi/type.go:48.2,50.12 2 1
+github.com/ethereum/go-ethereum/abi/type.go:51.2,52.60 1 0
+github.com/ethereum/go-ethereum/abi/type.go:56.16,58.3 1 0
+github.com/ethereum/go-ethereum/abi/type.go:64.55,67.3 2 1
+github.com/ethereum/go-ethereum/abi/type.go:69.13,72.16 3 1
+github.com/ethereum/go-ethereum/abi/type.go:73.3,74.21 1 0
+github.com/ethereum/go-ethereum/abi/type.go:75.3,76.22 1 1
+github.com/ethereum/go-ethereum/abi/type.go:77.3,78.66 1 0
+github.com/ethereum/go-ethereum/abi/type.go:80.3,81.16 1 1
+github.com/ethereum/go-ethereum/abi/type.go:82.3,86.17 4 0
+github.com/ethereum/go-ethereum/abi/type.go:87.3,91.18 4 1
+github.com/ethereum/go-ethereum/abi/type.go:92.3,93.27 1 1
+github.com/ethereum/go-ethereum/abi/type.go:94.3,95.30 1 0
+github.com/ethereum/go-ethereum/abi/type.go:96.3,100.21 4 1
+github.com/ethereum/go-ethereum/abi/type.go:101.3,104.17 3 1
+github.com/ethereum/go-ethereum/abi/type.go:107.3,108.60 1 0
+github.com/ethereum/go-ethereum/abi/type.go:104.17,106.5 1 1
+github.com/ethereum/go-ethereum/abi/type.go:116.37,118.2 1 1
+github.com/ethereum/go-ethereum/abi/type.go:126.51,128.36 2 1
+github.com/ethereum/go-ethereum/abi/type.go:179.2,179.20 1 0
+github.com/ethereum/go-ethereum/abi/type.go:129.2,130.23 1 1
+github.com/ethereum/go-ethereum/abi/type.go:133.3,133.34 1 1
+github.com/ethereum/go-ethereum/abi/type.go:134.2,135.23 1 1
+github.com/ethereum/go-ethereum/abi/type.go:138.3,138.34 1 1
+github.com/ethereum/go-ethereum/abi/type.go:139.2,142.49 1 1
+github.com/ethereum/go-ethereum/abi/type.go:145.3,145.34 1 1
+github.com/ethereum/go-ethereum/abi/type.go:146.2,147.42 1 1
+github.com/ethereum/go-ethereum/abi/type.go:150.3,150.60 1 1
+github.com/ethereum/go-ethereum/abi/type.go:151.2,152.42 1 1
+github.com/ethereum/go-ethereum/abi/type.go:157.3,157.23 1 1
+github.com/ethereum/go-ethereum/abi/type.go:162.3,162.78 1 1
+github.com/ethereum/go-ethereum/abi/type.go:166.3,167.36 2 1
+github.com/ethereum/go-ethereum/abi/type.go:170.3,170.21 1 1
+github.com/ethereum/go-ethereum/abi/type.go:171.2,172.19 1 1
+github.com/ethereum/go-ethereum/abi/type.go:130.23,132.4 1 0
+github.com/ethereum/go-ethereum/abi/type.go:135.23,137.4 1 0
+github.com/ethereum/go-ethereum/abi/type.go:142.49,144.4 1 1
+github.com/ethereum/go-ethereum/abi/type.go:147.42,149.4 1 1
+github.com/ethereum/go-ethereum/abi/type.go:152.42,154.4 1 1
+github.com/ethereum/go-ethereum/abi/type.go:157.23,159.4 1 1
+github.com/ethereum/go-ethereum/abi/type.go:162.78,164.4 1 0
+github.com/ethereum/go-ethereum/abi/type.go:167.36,169.4 1 1
+github.com/ethereum/go-ethereum/abi/type.go:172.19,174.4 1 1
+github.com/ethereum/go-ethereum/abi/type.go:174.4,176.4 1 0

+ 190 - 0
accounts/abi/type.go

@@ -0,0 +1,190 @@
+package abi
+
+import (
+	"fmt"
+	"reflect"
+	"regexp"
+	"strconv"
+
+	"github.com/ethereum/go-ethereum/ethutil"
+)
+
+const (
+	IntTy byte = iota
+	UintTy
+	BoolTy
+	SliceTy
+	AddressTy
+	RealTy
+)
+
+// Type is the reflection of the supported argument type
+type Type struct {
+	Kind       reflect.Kind
+	Type       reflect.Type
+	Size       int
+	T          byte   // Our own type checking
+	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.
+//
+// Strings can be in the format of:
+//
+// 	Input  = Type [ "[" [ Number ] "]" ] Name .
+// 	Type   = [ "u" ] "int" [ Number ] .
+//
+// Examples:
+//
+//      string     int       uint       real
+//      string32   int8      uint8      uint[]
+//      address    int256    uint256    real[2]
+func NewType(t string) (typ Type, err error) {
+	// 1. full string 2. type 3. (opt.) is slice 4. (opt.) size
+	freg, err := regexp.Compile("([a-zA-Z0-9]+)(\\[([0-9]*)?\\])?")
+	if err != nil {
+		return Type{}, err
+	}
+	res := freg.FindAllStringSubmatch(t, -1)[0]
+	var (
+		isslice bool
+		size    int
+	)
+	switch {
+	case res[3] != "":
+		// err is ignored. Already checked for number through the regexp
+		size, _ = strconv.Atoi(res[3])
+		isslice = true
+	case res[2] != "":
+		isslice = true
+		size = -1
+	case res[0] == "":
+		return Type{}, fmt.Errorf("type parse error for `%s`", t)
+	}
+
+	treg, err := regexp.Compile("([a-zA-Z]+)([0-9]*)?")
+	if err != nil {
+		return Type{}, err
+	}
+
+	parsedType := treg.FindAllStringSubmatch(res[1], -1)[0]
+	vsize, _ := strconv.Atoi(parsedType[2])
+	vtype := parsedType[1]
+	// substitute canonical representation
+	if vsize == 0 && (vtype == "int" || vtype == "uint") {
+		vsize = 256
+		t += "256"
+	}
+
+	if isslice {
+		typ.Kind = reflect.Slice
+		typ.Size = size
+		switch vtype {
+		case "int":
+			typ.Type = big_ts
+		case "uint":
+			typ.Type = ubig_ts
+		default:
+			return Type{}, fmt.Errorf("unsupported arg slice type: %s", t)
+		}
+	} else {
+		switch vtype {
+		case "int":
+			typ.Kind = reflect.Ptr
+			typ.Type = big_t
+			typ.Size = 256
+			typ.T = IntTy
+		case "uint":
+			typ.Kind = reflect.Ptr
+			typ.Type = ubig_t
+			typ.Size = 256
+			typ.T = UintTy
+		case "bool":
+			typ.Kind = reflect.Bool
+		case "real": // TODO
+			typ.Kind = reflect.Invalid
+		case "address":
+			typ.Kind = reflect.Slice
+			typ.Type = byte_ts
+			typ.Size = 20
+			typ.T = AddressTy
+		case "string":
+			typ.Kind = reflect.String
+			typ.Size = -1
+			if vsize > 0 {
+				typ.Size = 32
+			}
+		default:
+			return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+		}
+	}
+	typ.stringKind = t
+
+	return
+}
+
+func (t Type) String() (out string) {
+	return t.stringKind
+}
+
+// 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
+//   assignable
+// * Integer are checked for size
+// * Strings, addresses and bytes are checks for type and size
+func (t Type) pack(v interface{}) ([]byte, error) {
+	value := reflect.ValueOf(v)
+	switch kind := value.Kind(); kind {
+	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
+		if t.Type != ubig_t {
+			return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
+		}
+		return packNum(value, t.T), nil
+	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+		if t.Type != ubig_t {
+			return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
+		}
+		return packNum(value, t.T), nil
+	case reflect.Ptr:
+		// If the value is a ptr do a assign check (only used by
+		// big.Int for now)
+		if t.Type == ubig_t && value.Type() != ubig_t {
+			return nil, fmt.Errorf("type mismatch: %s for %T", t.Type, v)
+		}
+		return packNum(value, t.T), nil
+	case reflect.String:
+		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(ethutil.LeftPadString(t.String(), 32)), nil
+	case reflect.Slice:
+		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)
+		}
+
+		// Address is a special slice. The slice acts as one rather than a list of elements.
+		if t.T == AddressTy {
+			return ethutil.LeftPadBytes(v.([]byte), 32), nil
+		}
+
+		// Signed / Unsigned check
+		if (t.T != IntTy && isSigned(value)) || (t.T == UintTy && isSigned(value)) {
+			return nil, fmt.Errorf("slice of incompatible types.")
+		}
+
+		var packed []byte
+		for i := 0; i < value.Len(); i++ {
+			packed = append(packed, packNum(value.Index(i), t.T)...)
+		}
+		return packed, nil
+	case reflect.Bool:
+		if value.Bool() {
+			return ethutil.LeftPadBytes(ethutil.Big1.Bytes(), 32), nil
+		} else {
+			return ethutil.LeftPadBytes(ethutil.Big0.Bytes(), 32), nil
+		}
+	}
+
+	panic("unreached")
+}

+ 2 - 2
gocoverage.sh

@@ -15,7 +15,7 @@ if ls $dir/*.go &> /dev/null; then
     # echo $dir
     if [[ $dir != "./tests/vm" ]]
     then
-        go test -covermode=count -coverprofile=$dir/profile.tmp $dir
+        $GOROOT/bin/go test -covermode=count -coverprofile=$dir/profile.tmp $dir
     fi
     if [ -f $dir/profile.tmp ]
     then
@@ -25,7 +25,7 @@ if ls $dir/*.go &> /dev/null; then
 fi
 done
 
-go tool cover -func profile.cov
+$GOROOT/bin/go tool cover -func profile.cov
 
 # To submit the test coverage result to coveralls.io,
 # use goveralls (https://github.com/mattn/goveralls)