mirror of https://github.com/ethereum/go-ethereum
commit
8284bef072
@ -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 |
||||||
|
} |
@ -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) |
||||||
|
} |
||||||
|
} |
@ -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 |
@ -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 |
||||||
|
} |
@ -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() |
||||||
|
} |
||||||
|
} |
@ -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 |
@ -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") |
||||||
|
} |
@ -0,0 +1,95 @@ |
|||||||
|
/* |
||||||
|
This file is part of go-ethereum |
||||||
|
|
||||||
|
go-ethereum is free software: you can redistribute it and/or modify |
||||||
|
it under the terms of the GNU Lesser General Public License as published by |
||||||
|
the Free Software Foundation, either version 3 of the License, or |
||||||
|
(at your option) any later version. |
||||||
|
|
||||||
|
go-ethereum is distributed in the hope that it will be useful, |
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
GNU General Public License for more details. |
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public License |
||||||
|
along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/ |
||||||
|
/** |
||||||
|
* @authors |
||||||
|
* Gustav Simonsson <gustav.simonsson@gmail.com> |
||||||
|
* @date 2015 |
||||||
|
* |
||||||
|
*/ |
||||||
|
/* |
||||||
|
|
||||||
|
This abstracts part of a user's interaction with an account she controls. |
||||||
|
It's not an abstraction of core Ethereum accounts data type / logic - |
||||||
|
for that see the core processing code of blocks / txs. |
||||||
|
|
||||||
|
Currently this is pretty much a passthrough to the KeyStore2 interface, |
||||||
|
and accounts persistence is derived from stored keys' addresses |
||||||
|
|
||||||
|
*/ |
||||||
|
package accounts |
||||||
|
|
||||||
|
import ( |
||||||
|
crand "crypto/rand" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
) |
||||||
|
|
||||||
|
// TODO: better name for this struct?
|
||||||
|
type Account struct { |
||||||
|
Address []byte |
||||||
|
} |
||||||
|
|
||||||
|
type AccountManager struct { |
||||||
|
keyStore crypto.KeyStore2 |
||||||
|
} |
||||||
|
|
||||||
|
// TODO: get key by addr - modify KeyStore2 GetKey to work with addr
|
||||||
|
|
||||||
|
// TODO: pass through passphrase for APIs which require access to private key?
|
||||||
|
func NewAccountManager(keyStore crypto.KeyStore2) AccountManager { |
||||||
|
am := &AccountManager{ |
||||||
|
keyStore: keyStore, |
||||||
|
} |
||||||
|
return *am |
||||||
|
} |
||||||
|
|
||||||
|
func (am *AccountManager) Sign(fromAccount *Account, keyAuth string, toSign []byte) (signature []byte, err error) { |
||||||
|
key, err := am.keyStore.GetKey(fromAccount.Address, keyAuth) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
signature, err = crypto.Sign(toSign, key.PrivateKey) |
||||||
|
return signature, err |
||||||
|
} |
||||||
|
|
||||||
|
func (am AccountManager) NewAccount(auth string) (*Account, error) { |
||||||
|
key, err := am.keyStore.GenerateNewKey(crand.Reader, auth) |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
ua := &Account{ |
||||||
|
Address: key.Address, |
||||||
|
} |
||||||
|
return ua, err |
||||||
|
} |
||||||
|
|
||||||
|
// set of accounts == set of keys in given key store
|
||||||
|
// TODO: do we need persistence of accounts as well?
|
||||||
|
func (am *AccountManager) Accounts() ([]Account, error) { |
||||||
|
addresses, err := am.keyStore.GetKeyAddresses() |
||||||
|
if err != nil { |
||||||
|
return nil, err |
||||||
|
} |
||||||
|
|
||||||
|
accounts := make([]Account, len(addresses)) |
||||||
|
|
||||||
|
for i, addr := range addresses { |
||||||
|
accounts[i] = Account{ |
||||||
|
Address: addr, |
||||||
|
} |
||||||
|
} |
||||||
|
return accounts, err |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
package accounts |
||||||
|
|
||||||
|
import ( |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"testing" |
||||||
|
) |
||||||
|
|
||||||
|
func TestAccountManager(t *testing.T) { |
||||||
|
ks := crypto.NewKeyStorePlain(crypto.DefaultDataDir()) |
||||||
|
am := NewAccountManager(ks) |
||||||
|
pass := "" // not used but required by API
|
||||||
|
a1, err := am.NewAccount(pass) |
||||||
|
toSign := crypto.GetEntropyCSPRNG(32) |
||||||
|
_, err = am.Sign(a1, pass, toSign) |
||||||
|
if err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue