diff options
Diffstat (limited to 'accounts/abi')
-rw-r--r-- | accounts/abi/abi.go | 155 | ||||
-rw-r--r-- | accounts/abi/abi_test.go | 330 | ||||
-rw-r--r-- | accounts/abi/doc.go | 10 | ||||
-rw-r--r-- | accounts/abi/numbers.go | 106 | ||||
-rw-r--r-- | accounts/abi/numbers_test.go | 72 | ||||
-rw-r--r-- | accounts/abi/type.go | 190 |
6 files changed, 863 insertions, 0 deletions
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go new file mode 100644 index 000000000..067381a48 --- /dev/null +++ b/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 +} diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go new file mode 100644 index 000000000..f47fa80f0 --- /dev/null +++ b/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) + } +} diff --git a/accounts/abi/doc.go b/accounts/abi/doc.go new file mode 100644 index 000000000..648c971e1 --- /dev/null +++ b/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 diff --git a/accounts/abi/numbers.go b/accounts/abi/numbers.go new file mode 100644 index 000000000..9205c005e --- /dev/null +++ b/accounts/abi/numbers.go @@ -0,0 +1,106 @@ +package abi + +import ( + "math/big" + "reflect" + + "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 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 common.LeftPadBytes(common.U256(n).Bytes(), 32) +} + +func S256(n *big.Int) []byte { + sint := common.S256(n) + ret := common.LeftPadBytes(sint.Bytes(), 32) + if sint.Cmp(common.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 +} diff --git a/accounts/abi/numbers_test.go b/accounts/abi/numbers_test.go new file mode 100644 index 000000000..319d7fb51 --- /dev/null +++ b/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() + } +} diff --git a/accounts/abi/type.go b/accounts/abi/type.go new file mode 100644 index 000000000..56520b672 --- /dev/null +++ b/accounts/abi/type.go @@ -0,0 +1,190 @@ +package abi + +import ( + "fmt" + "reflect" + "regexp" + "strconv" + + "github.com/ethereum/go-ethereum/common" +) + +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(common.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 common.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 common.LeftPadBytes(common.Big1.Bytes(), 32), nil + } else { + return common.LeftPadBytes(common.Big0.Bytes(), 32), nil + } + } + + panic("unreached") +} |