aboutsummaryrefslogtreecommitdiffstats
path: root/accounts
diff options
context:
space:
mode:
Diffstat (limited to 'accounts')
-rw-r--r--accounts/abi/abi.go115
-rw-r--r--accounts/abi/abi_test.go21
-rw-r--r--accounts/abi/bind/backends/simulated.go39
-rw-r--r--accounts/abi/bind/bind.go2
-rw-r--r--accounts/abi/error.go24
-rw-r--r--accounts/abi/event.go91
-rw-r--r--accounts/abi/event_test.go2
-rw-r--r--accounts/abi/method.go79
-rw-r--r--accounts/abi/numbers.go47
-rw-r--r--accounts/abi/pack.go7
-rw-r--r--accounts/abi/pack_test.go7
-rw-r--r--accounts/abi/reflect.go10
-rw-r--r--accounts/abi/type.go210
-rw-r--r--accounts/abi/type_test.go215
-rw-r--r--accounts/abi/unpack.go281
-rw-r--r--accounts/abi/unpack_test.go641
-rw-r--r--accounts/keystore/account_cache.go113
-rw-r--r--accounts/keystore/account_cache_test.go12
-rw-r--r--accounts/keystore/file_cache.go102
-rw-r--r--accounts/keystore/keystore_passphrase.go7
-rw-r--r--accounts/keystore/presale.go3
-rw-r--r--accounts/keystore/watch.go6
-rw-r--r--accounts/manager.go10
23 files changed, 1086 insertions, 958 deletions
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 2a06d474b..205dc300b 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -20,10 +20,6 @@ import (
"encoding/json"
"fmt"
"io"
- "reflect"
- "strings"
-
- "github.com/ethereum/go-ethereum/common"
)
// The ABI holds information about a contract's context and available
@@ -76,106 +72,27 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return append(method.Id(), arguments...), nil
}
-// these variable are used to determine certain types during type assertion for
-// assignment.
-var (
- r_interSlice = reflect.TypeOf([]interface{}{})
- r_hash = reflect.TypeOf(common.Hash{})
- r_bytes = reflect.TypeOf([]byte{})
- r_byte = reflect.TypeOf(byte(0))
-)
-
// Unpack output in v according to the abi specification
-func (abi ABI) Unpack(v interface{}, name string, output []byte) error {
- var method = abi.Methods[name]
-
- if len(output) == 0 {
- return fmt.Errorf("abi: unmarshalling empty output")
- }
-
- // make sure the passed value is a pointer
- valueOf := reflect.ValueOf(v)
- if reflect.Ptr != valueOf.Kind() {
- return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
+ if err = bytesAreProper(output); err != nil {
+ return err
}
-
- var (
- value = valueOf.Elem()
- typ = value.Type()
- )
-
- if len(method.Outputs) > 1 {
- switch value.Kind() {
- // struct will match named return values to the struct's field
- // names
- case reflect.Struct:
- for i := 0; i < len(method.Outputs); i++ {
- marshalledValue, err := toGoType(i, method.Outputs[i], output)
- if err != nil {
- return err
- }
- reflectValue := reflect.ValueOf(marshalledValue)
-
- for j := 0; j < typ.NumField(); j++ {
- field := typ.Field(j)
- // TODO read tags: `abi:"fieldName"`
- if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
- if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil {
- return err
- }
- }
- }
- }
- case reflect.Slice:
- if !value.Type().AssignableTo(r_interSlice) {
- return fmt.Errorf("abi: cannot marshal tuple in to slice %T (only []interface{} is supported)", v)
- }
-
- // if the slice already contains values, set those instead of the interface slice itself.
- if value.Len() > 0 {
- if len(method.Outputs) > value.Len() {
- return fmt.Errorf("abi: cannot marshal in to slices of unequal size (require: %v, got: %v)", len(method.Outputs), value.Len())
- }
-
- for i := 0; i < len(method.Outputs); i++ {
- marshalledValue, err := toGoType(i, method.Outputs[i], output)
- if err != nil {
- return err
- }
- reflectValue := reflect.ValueOf(marshalledValue)
- if err := set(value.Index(i).Elem(), reflectValue, method.Outputs[i]); err != nil {
- return err
- }
- }
- return nil
- }
-
- // create a new slice and start appending the unmarshalled
- // values to the new interface slice.
- z := reflect.MakeSlice(typ, 0, len(method.Outputs))
- for i := 0; i < len(method.Outputs); i++ {
- marshalledValue, err := toGoType(i, method.Outputs[i], output)
- if err != nil {
- return err
- }
- z = reflect.Append(z, reflect.ValueOf(marshalledValue))
- }
- value.Set(z)
- default:
- return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
- }
-
+ // since there can't be naming collisions with contracts and events,
+ // we need to decide whether we're calling a method or an event
+ var unpack unpacker
+ if method, ok := abi.Methods[name]; ok {
+ unpack = method
+ } else if event, ok := abi.Events[name]; ok {
+ unpack = event
} else {
- marshalledValue, err := toGoType(0, method.Outputs[0], output)
- if err != nil {
- return err
- }
- if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
- return err
- }
+ return fmt.Errorf("abi: could not locate named method or event.")
}
- return nil
+ // requires a struct to unpack into for a tuple return...
+ if unpack.isTupleReturn() {
+ return unpack.tupleUnpack(v, output)
+ }
+ return unpack.singleUnpack(v, output)
}
func (abi *ABI) UnmarshalJSON(data []byte) error {
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index 5420eb19a..2ae7488a4 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -29,25 +29,6 @@ import (
"github.com/ethereum/go-ethereum/crypto"
)
-// formatSilceOutput add padding to the value and adds a size
-func formatSliceOutput(v ...[]byte) []byte {
- off := common.LeftPadBytes(big.NewInt(int64(len(v))).Bytes(), 32)
- output := append(off, make([]byte, 0, len(v)*32)...)
-
- for _, value := range v {
- output = append(output, common.LeftPadBytes(value, 32)...)
- }
- return output
-}
-
-// quick helper padding
-func pad(input []byte, size int, left bool) []byte {
- if left {
- return common.LeftPadBytes(input, size)
- }
- return common.RightPadBytes(input, size)
-}
-
const jsondata = `
[
{ "type" : "function", "name" : "balance", "constant" : true },
@@ -191,7 +172,7 @@ func TestMethodSignature(t *testing.T) {
t.Errorf("expected ids to match %x != %x", m.Id(), idexp)
}
- uintt, _ := NewType("uint")
+ uintt, _ := NewType("uint256")
m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil}
exp = "foo(uint256)"
if m.Sig() != exp {
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index 0621e81c2..09288d401 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -41,6 +41,7 @@ import (
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
var errBlockNumberUnsupported = errors.New("SimulatedBackend cannot access blocks other than the latest block")
+var errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
// the background. Its main purpose is to allow easily testing contract bindings.
@@ -59,7 +60,7 @@ type SimulatedBackend struct {
// for testing purposes.
func NewSimulatedBackend(alloc core.GenesisAlloc) *SimulatedBackend {
database, _ := ethdb.NewMemDatabase()
- genesis := core.Genesis{Config: params.AllProtocolChanges, Alloc: alloc}
+ genesis := core.Genesis{Config: params.AllEthashProtocolChanges, Alloc: alloc}
genesis.MustCommit(database)
blockchain, _ := core.NewBlockChain(database, genesis.Config, ethash.NewFaker(), vm.Config{})
backend := &SimulatedBackend{database: database, blockchain: blockchain, config: genesis.Config}
@@ -203,32 +204,46 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
b.mu.Lock()
defer b.mu.Unlock()
- // Binary search the gas requirement, as it may be higher than the amount used
+ // Determine the lowest and highest possible gas limits to binary search in between
var (
- lo uint64 = params.TxGas - 1
- hi uint64
+ lo uint64 = params.TxGas - 1
+ hi uint64
+ cap uint64
)
if call.Gas != nil && call.Gas.Uint64() >= params.TxGas {
hi = call.Gas.Uint64()
} else {
hi = b.pendingBlock.GasLimit().Uint64()
}
- for lo+1 < hi {
- // Take a guess at the gas, and check transaction validity
- mid := (hi + lo) / 2
- call.Gas = new(big.Int).SetUint64(mid)
+ cap = hi
+
+ // Create a helper to check if a gas allowance results in an executable transaction
+ executable := func(gas uint64) bool {
+ call.Gas = new(big.Int).SetUint64(gas)
snapshot := b.pendingState.Snapshot()
_, _, failed, err := b.callContract(ctx, call, b.pendingBlock, b.pendingState)
b.pendingState.RevertToSnapshot(snapshot)
- // If the transaction became invalid or execution failed, raise the gas limit
if err != nil || failed {
+ return false
+ }
+ return true
+ }
+ // Execute the binary search and hone in on an executable gas limit
+ for lo+1 < hi {
+ mid := (hi + lo) / 2
+ if !executable(mid) {
lo = mid
- continue
+ } else {
+ hi = mid
+ }
+ }
+ // Reject the transaction as invalid if it still fails at the highest allowance
+ if hi == cap {
+ if !executable(hi) {
+ return nil, errGasEstimationFailed
}
- // Otherwise assume the transaction succeeded, lower the gas limit
- hi = mid
}
return new(big.Int).SetUint64(hi), nil
}
diff --git a/accounts/abi/bind/bind.go b/accounts/abi/bind/bind.go
index f58758088..4dce79b77 100644
--- a/accounts/abi/bind/bind.go
+++ b/accounts/abi/bind/bind.go
@@ -129,7 +129,7 @@ func Bind(types []string, abis []string, bytecodes []string, pkg string, lang La
return string(code), nil
}
// For all others just return as is for now
- return string(buffer.Bytes()), nil
+ return buffer.String(), nil
}
// bindType is a set of type binders that convert Solidity types to some supported
diff --git a/accounts/abi/error.go b/accounts/abi/error.go
index 420acf418..9d8674ad0 100644
--- a/accounts/abi/error.go
+++ b/accounts/abi/error.go
@@ -39,22 +39,23 @@ func formatSliceString(kind reflect.Kind, sliceSize int) string {
// type in t.
func sliceTypeCheck(t Type, val reflect.Value) error {
if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
- return typeErr(formatSliceString(t.Kind, t.SliceSize), val.Type())
+ return typeErr(formatSliceString(t.Kind, t.Size), val.Type())
}
- if t.IsArray && val.Len() != t.SliceSize {
- return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), formatSliceString(val.Type().Elem().Kind(), val.Len()))
+
+ if t.T == ArrayTy && val.Len() != t.Size {
+ return typeErr(formatSliceString(t.Elem.Kind, t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
}
- if t.Elem.IsSlice {
+ if t.Elem.T == SliceTy {
if val.Len() > 0 {
return sliceTypeCheck(*t.Elem, val.Index(0))
}
- } else if t.Elem.IsArray {
+ } else if t.Elem.T == ArrayTy {
return sliceTypeCheck(*t.Elem, val.Index(0))
}
if elemKind := val.Type().Elem().Kind(); elemKind != t.Elem.Kind {
- return typeErr(formatSliceString(t.Elem.Kind, t.SliceSize), val.Type())
+ return typeErr(formatSliceString(t.Elem.Kind, t.Size), val.Type())
}
return nil
}
@@ -62,20 +63,19 @@ func sliceTypeCheck(t Type, val reflect.Value) error {
// typeCheck checks that the given reflection value can be assigned to the reflection
// type in t.
func typeCheck(t Type, value reflect.Value) error {
- if t.IsSlice || t.IsArray {
+ if t.T == SliceTy || t.T == ArrayTy {
return sliceTypeCheck(t, value)
}
// Check base type validity. Element types will be checked later on.
if t.Kind != value.Kind() {
return typeErr(t.Kind, value.Kind())
+ } else if t.T == FixedBytesTy && t.Size != value.Len() {
+ return typeErr(t.Type, value.Type())
+ } else {
+ return nil
}
- return nil
-}
-// varErr returns a formatted error.
-func varErr(expected, got reflect.Kind) error {
- return typeErr(expected, got)
}
// typeErr returns a formatted type casting error.
diff --git a/accounts/abi/event.go b/accounts/abi/event.go
index 51ab84241..44ed7b8df 100644
--- a/accounts/abi/event.go
+++ b/accounts/abi/event.go
@@ -18,6 +18,7 @@ package abi
import (
"fmt"
+ "reflect"
"strings"
"github.com/ethereum/go-ethereum/common"
@@ -44,3 +45,93 @@ func (e Event) Id() common.Hash {
}
return common.BytesToHash(crypto.Keccak256([]byte(fmt.Sprintf("%v(%v)", e.Name, strings.Join(types, ",")))))
}
+
+// unpacks an event return tuple into a struct of corresponding go types
+//
+// Unpacking can be done into a struct or a slice/array.
+func (e Event) tupleUnpack(v interface{}, output []byte) error {
+ // make sure the passed value is a pointer
+ valueOf := reflect.ValueOf(v)
+ if reflect.Ptr != valueOf.Kind() {
+ return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+ }
+
+ var (
+ value = valueOf.Elem()
+ typ = value.Type()
+ )
+
+ if value.Kind() != reflect.Struct {
+ return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
+ }
+
+ j := 0
+ for i := 0; i < len(e.Inputs); i++ {
+ input := e.Inputs[i]
+ if input.Indexed {
+ // can't read, continue
+ continue
+ } else if input.Type.T == ArrayTy {
+ // need to move this up because they read sequentially
+ j += input.Type.Size
+ }
+ marshalledValue, err := toGoType((i+j)*32, input.Type, output)
+ if err != nil {
+ return err
+ }
+ reflectValue := reflect.ValueOf(marshalledValue)
+
+ switch value.Kind() {
+ case reflect.Struct:
+ for j := 0; j < typ.NumField(); j++ {
+ field := typ.Field(j)
+ // TODO read tags: `abi:"fieldName"`
+ if field.Name == strings.ToUpper(e.Inputs[i].Name[:1])+e.Inputs[i].Name[1:] {
+ if err := set(value.Field(j), reflectValue, e.Inputs[i]); err != nil {
+ return err
+ }
+ }
+ }
+ case reflect.Slice, reflect.Array:
+ if value.Len() < i {
+ return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(e.Inputs), value.Len())
+ }
+ v := value.Index(i)
+ if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
+ return fmt.Errorf("abi: cannot unmarshal %v in to %v", v.Type(), reflectValue.Type())
+ }
+ reflectValue := reflect.ValueOf(marshalledValue)
+ if err := set(v.Elem(), reflectValue, e.Inputs[i]); err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
+ }
+ }
+ return nil
+}
+
+func (e Event) isTupleReturn() bool { return len(e.Inputs) > 1 }
+
+func (e Event) singleUnpack(v interface{}, output []byte) error {
+ // make sure the passed value is a pointer
+ valueOf := reflect.ValueOf(v)
+ if reflect.Ptr != valueOf.Kind() {
+ return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+ }
+
+ if e.Inputs[0].Indexed {
+ return fmt.Errorf("abi: attempting to unpack indexed variable into element.")
+ }
+
+ value := valueOf.Elem()
+
+ marshalledValue, err := toGoType(0, e.Inputs[0].Type, output)
+ if err != nil {
+ return err
+ }
+ if err := set(value, reflect.ValueOf(marshalledValue), e.Inputs[0]); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/accounts/abi/event_test.go b/accounts/abi/event_test.go
index b5054a032..7e2f13f76 100644
--- a/accounts/abi/event_test.go
+++ b/accounts/abi/event_test.go
@@ -31,7 +31,7 @@ func TestEventId(t *testing.T) {
}{
{
definition: `[
- { "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint" }] },
+ { "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
{ "type" : "event", "name" : "check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] }
]`,
expectations: map[string]common.Hash{
diff --git a/accounts/abi/method.go b/accounts/abi/method.go
index 32077e8a6..609a71f07 100644
--- a/accounts/abi/method.go
+++ b/accounts/abi/method.go
@@ -88,6 +88,85 @@ func (method Method) pack(args ...interface{}) ([]byte, error) {
return ret, nil
}
+// unpacks a method return tuple into a struct of corresponding go types
+//
+// Unpacking can be done into a struct or a slice/array.
+func (method Method) tupleUnpack(v interface{}, output []byte) error {
+ // make sure the passed value is a pointer
+ valueOf := reflect.ValueOf(v)
+ if reflect.Ptr != valueOf.Kind() {
+ return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+ }
+
+ var (
+ value = valueOf.Elem()
+ typ = value.Type()
+ )
+
+ j := 0
+ for i := 0; i < len(method.Outputs); i++ {
+ toUnpack := method.Outputs[i]
+ if toUnpack.Type.T == ArrayTy {
+ // need to move this up because they read sequentially
+ j += toUnpack.Type.Size
+ }
+ marshalledValue, err := toGoType((i+j)*32, toUnpack.Type, output)
+ if err != nil {
+ return err
+ }
+ reflectValue := reflect.ValueOf(marshalledValue)
+
+ switch value.Kind() {
+ case reflect.Struct:
+ for j := 0; j < typ.NumField(); j++ {
+ field := typ.Field(j)
+ // TODO read tags: `abi:"fieldName"`
+ if field.Name == strings.ToUpper(method.Outputs[i].Name[:1])+method.Outputs[i].Name[1:] {
+ if err := set(value.Field(j), reflectValue, method.Outputs[i]); err != nil {
+ return err
+ }
+ }
+ }
+ case reflect.Slice, reflect.Array:
+ if value.Len() < i {
+ return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(method.Outputs), value.Len())
+ }
+ v := value.Index(i)
+ if v.Kind() != reflect.Ptr && v.Kind() != reflect.Interface {
+ return fmt.Errorf("abi: cannot unmarshal %v in to %v", v.Type(), reflectValue.Type())
+ }
+ reflectValue := reflect.ValueOf(marshalledValue)
+ if err := set(v.Elem(), reflectValue, method.Outputs[i]); err != nil {
+ return err
+ }
+ default:
+ return fmt.Errorf("abi: cannot unmarshal tuple in to %v", typ)
+ }
+ }
+ return nil
+}
+
+func (method Method) isTupleReturn() bool { return len(method.Outputs) > 1 }
+
+func (method Method) singleUnpack(v interface{}, output []byte) error {
+ // make sure the passed value is a pointer
+ valueOf := reflect.ValueOf(v)
+ if reflect.Ptr != valueOf.Kind() {
+ return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
+ }
+
+ value := valueOf.Elem()
+
+ marshalledValue, err := toGoType(0, method.Outputs[0].Type, output)
+ if err != nil {
+ return err
+ }
+ if err := set(value, reflect.ValueOf(marshalledValue), method.Outputs[0]); err != nil {
+ return err
+ }
+ return nil
+}
+
// Sig returns the methods string signature according to the ABI spec.
//
// Example
diff --git a/accounts/abi/numbers.go b/accounts/abi/numbers.go
index 5d3efff52..9ad99f90d 100644
--- a/accounts/abi/numbers.go
+++ b/accounts/abi/numbers.go
@@ -25,36 +25,23 @@ import (
)
var (
- big_t = reflect.TypeOf(big.Int{})
- ubig_t = reflect.TypeOf(big.Int{})
- byte_t = reflect.TypeOf(byte(0))
- byte_ts = reflect.TypeOf([]byte(nil))
- uint_t = reflect.TypeOf(uint(0))
- uint8_t = reflect.TypeOf(uint8(0))
- uint16_t = reflect.TypeOf(uint16(0))
- uint32_t = reflect.TypeOf(uint32(0))
- uint64_t = reflect.TypeOf(uint64(0))
- int_t = reflect.TypeOf(int(0))
- int8_t = reflect.TypeOf(int8(0))
- int16_t = reflect.TypeOf(int16(0))
- int32_t = reflect.TypeOf(int32(0))
- int64_t = reflect.TypeOf(int64(0))
- hash_t = reflect.TypeOf(common.Hash{})
- address_t = reflect.TypeOf(common.Address{})
-
- uint_ts = reflect.TypeOf([]uint(nil))
- uint8_ts = reflect.TypeOf([]uint8(nil))
- uint16_ts = reflect.TypeOf([]uint16(nil))
- uint32_ts = reflect.TypeOf([]uint32(nil))
- uint64_ts = reflect.TypeOf([]uint64(nil))
- ubig_ts = reflect.TypeOf([]*big.Int(nil))
-
- int_ts = reflect.TypeOf([]int(nil))
- int8_ts = reflect.TypeOf([]int8(nil))
- int16_ts = reflect.TypeOf([]int16(nil))
- int32_ts = reflect.TypeOf([]int32(nil))
- int64_ts = reflect.TypeOf([]int64(nil))
- big_ts = reflect.TypeOf([]*big.Int(nil))
+ big_t = reflect.TypeOf(&big.Int{})
+ derefbig_t = reflect.TypeOf(big.Int{})
+ uint8_t = reflect.TypeOf(uint8(0))
+ uint16_t = reflect.TypeOf(uint16(0))
+ uint32_t = reflect.TypeOf(uint32(0))
+ uint64_t = reflect.TypeOf(uint64(0))
+ int_t = reflect.TypeOf(int(0))
+ int8_t = reflect.TypeOf(int8(0))
+ int16_t = reflect.TypeOf(int16(0))
+ int32_t = reflect.TypeOf(int32(0))
+ int64_t = reflect.TypeOf(int64(0))
+ address_t = reflect.TypeOf(common.Address{})
+ int_ts = reflect.TypeOf([]int(nil))
+ int8_ts = reflect.TypeOf([]int8(nil))
+ int16_ts = reflect.TypeOf([]int16(nil))
+ int32_ts = reflect.TypeOf([]int32(nil))
+ int64_ts = reflect.TypeOf([]int64(nil))
)
// U256 converts a big Int into a 256bit EVM number.
diff --git a/accounts/abi/pack.go b/accounts/abi/pack.go
index 4d8a3f031..072e80536 100644
--- a/accounts/abi/pack.go
+++ b/accounts/abi/pack.go
@@ -61,8 +61,9 @@ func packElement(t Type, reflectValue reflect.Value) []byte {
reflectValue = mustArrayToByteSlice(reflectValue)
}
return common.RightPadBytes(reflectValue.Bytes(), 32)
+ default:
+ panic("abi: fatal error")
}
- panic("abi: fatal error")
}
// packNum packs the given number (using the reflect value) and will cast it to appropriate number representation
@@ -74,6 +75,8 @@ func packNum(value reflect.Value) []byte {
return U256(big.NewInt(value.Int()))
case reflect.Ptr:
return U256(value.Interface().(*big.Int))
+ default:
+ panic("abi: fatal error")
}
- return nil
+
}
diff --git a/accounts/abi/pack_test.go b/accounts/abi/pack_test.go
index c6cfb56ea..36401ee67 100644
--- a/accounts/abi/pack_test.go
+++ b/accounts/abi/pack_test.go
@@ -322,12 +322,12 @@ func TestPack(t *testing.T) {
} {
typ, err := NewType(test.typ)
if err != nil {
- t.Fatal("unexpected parse error:", err)
+ t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
}
output, err := typ.pack(reflect.ValueOf(test.input))
if err != nil {
- t.Fatal("unexpected pack error:", err)
+ t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
}
if !bytes.Equal(output, test.output) {
@@ -435,7 +435,4 @@ func TestPackNumber(t *testing.T) {
t.Errorf("test %d: pack mismatch: have %x, want %x", i, packed, tt.packed)
}
}
- if packed := packNum(reflect.ValueOf("string")); packed != nil {
- t.Errorf("expected 'string' to pack to nil. got %x instead", packed)
- }
}
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
index 8fa75df07..e953b77c1 100644
--- a/accounts/abi/reflect.go
+++ b/accounts/abi/reflect.go
@@ -24,7 +24,7 @@ import (
// indirect recursively dereferences the value until it either gets the value
// or finds a big.Int
func indirect(v reflect.Value) reflect.Value {
- if v.Kind() == reflect.Ptr && v.Elem().Type() != big_t {
+ if v.Kind() == reflect.Ptr && v.Elem().Type() != derefbig_t {
return indirect(v.Elem())
}
return v
@@ -73,15 +73,9 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
func set(dst, src reflect.Value, output Argument) error {
dstType := dst.Type()
srcType := src.Type()
-
switch {
- case dstType.AssignableTo(src.Type()):
+ case dstType.AssignableTo(srcType):
dst.Set(src)
- case dstType.Kind() == reflect.Array && srcType.Kind() == reflect.Slice:
- if dst.Len() < output.Type.SliceSize {
- return fmt.Errorf("abi: cannot unmarshal src (len=%d) in to dst (len=%d)", output.Type.SliceSize, dst.Len())
- }
- reflect.Copy(dst, src)
case dstType.Kind() == reflect.Interface:
dst.Set(src)
case dstType.Kind() == reflect.Ptr:
diff --git a/accounts/abi/type.go b/accounts/abi/type.go
index 5f20babb3..fba10b96d 100644
--- a/accounts/abi/type.go
+++ b/accounts/abi/type.go
@@ -21,6 +21,7 @@ import (
"reflect"
"regexp"
"strconv"
+ "strings"
)
const (
@@ -29,6 +30,7 @@ const (
BoolTy
StringTy
SliceTy
+ ArrayTy
AddressTy
FixedBytesTy
BytesTy
@@ -39,9 +41,6 @@ const (
// Type is the reflection of the supported argument type
type Type struct {
- IsSlice, IsArray bool
- SliceSize int
-
Elem *Type
Kind reflect.Kind
@@ -53,118 +52,116 @@ type Type struct {
}
var (
- // fullTypeRegex parses the abi types
- //
- // Types can be in the format of:
- //
- // Input = Type [ "[" [ Number ] "]" ] Name .
- // Type = [ "u" ] "int" [ Number ] [ x ] [ Number ].
- //
- // Examples:
- //
- // string int uint fixed
- // string32 int8 uint8 uint[]
- // address int256 uint256 fixed128x128[2]
- fullTypeRegex = regexp.MustCompile(`([a-zA-Z0-9]+)(\[([0-9]*)\])?`)
// typeRegex parses the abi sub types
typeRegex = regexp.MustCompile("([a-zA-Z]+)(([0-9]+)(x([0-9]+))?)?")
)
// NewType creates a new reflection type of abi type given in t.
func NewType(t string) (typ Type, err error) {
- res := fullTypeRegex.FindAllStringSubmatch(t, -1)[0]
- // check if type is slice and parse type.
- switch {
- case res[3] != "":
- // err is ignored. Already checked for number through the regexp
- typ.SliceSize, _ = strconv.Atoi(res[3])
- typ.IsArray = true
- case res[2] != "":
- typ.IsSlice, typ.SliceSize = true, -1
- case res[0] == "":
- return Type{}, fmt.Errorf("abi: type parse error: %s", t)
+ // check that array brackets are equal if they exist
+ if strings.Count(t, "[") != strings.Count(t, "]") {
+ return Type{}, fmt.Errorf("invalid arg type in abi")
}
- if typ.IsArray || typ.IsSlice {
- sliceType, err := NewType(res[1])
+
+ typ.stringKind = t
+
+ // if there are brackets, get ready to go into slice/array mode and
+ // recursively create the type
+ if strings.Count(t, "[") != 0 {
+ i := strings.LastIndex(t, "[")
+ // recursively embed the type
+ embeddedType, err := NewType(t[:i])
if err != nil {
return Type{}, err
}
- typ.Elem = &sliceType
- typ.stringKind = sliceType.stringKind + t[len(res[1]):]
- // Although we know that this is an array, we cannot return
- // as we don't know the type of the element, however, if it
- // is still an array, then don't determine the type.
- if typ.Elem.IsArray || typ.Elem.IsSlice {
- return typ, nil
- }
- }
-
- // parse the type and size of the abi-type.
- parsedType := typeRegex.FindAllStringSubmatch(res[1], -1)[0]
- // varSize is the size of the variable
- var varSize int
- if len(parsedType[3]) > 0 {
- var err error
- varSize, err = strconv.Atoi(parsedType[2])
- if err != nil {
- return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ // grab the last cell and create a type from there
+ sliced := t[i:]
+ // grab the slice size with regexp
+ re := regexp.MustCompile("[0-9]+")
+ intz := re.FindAllString(sliced, -1)
+
+ if len(intz) == 0 {
+ // is a slice
+ typ.T = SliceTy
+ typ.Kind = reflect.Slice
+ typ.Elem = &embeddedType
+ typ.Type = reflect.SliceOf(embeddedType.Type)
+ } else if len(intz) == 1 {
+ // is a array
+ typ.T = ArrayTy
+ typ.Kind = reflect.Array
+ typ.Elem = &embeddedType
+ typ.Size, err = strconv.Atoi(intz[0])
+ if err != nil {
+ return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ }
+ typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
+ } else {
+ return Type{}, fmt.Errorf("invalid formatting of array type")
}
- }
- // varType is the parsed abi type
- varType := parsedType[1]
- // substitute canonical integer
- if varSize == 0 && (varType == "int" || varType == "uint") {
- varSize = 256
- t += "256"
- }
-
- // only set stringKind if not array or slice, as for those,
- // the correct string type has been set
- if !(typ.IsArray || typ.IsSlice) {
- typ.stringKind = t
- }
-
- switch varType {
- case "int":
- typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
- typ.Size = varSize
- typ.T = IntTy
- case "uint":
- typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
- typ.Size = varSize
- typ.T = UintTy
- case "bool":
- typ.Kind = reflect.Bool
- typ.T = BoolTy
- case "address":
- typ.Kind = reflect.Array
- typ.Type = address_t
- typ.Size = 20
- typ.T = AddressTy
- case "string":
- typ.Kind = reflect.String
- typ.Size = -1
- typ.T = StringTy
- case "bytes":
- sliceType, _ := NewType("uint8")
- typ.Elem = &sliceType
- if varSize == 0 {
- typ.IsSlice = true
- typ.T = BytesTy
- typ.SliceSize = -1
+ return typ, err
+ } else {
+ // parse the type and size of the abi-type.
+ parsedType := typeRegex.FindAllStringSubmatch(t, -1)[0]
+ // varSize is the size of the variable
+ var varSize int
+ if len(parsedType[3]) > 0 {
+ var err error
+ varSize, err = strconv.Atoi(parsedType[2])
+ if err != nil {
+ return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
+ }
} else {
- typ.IsArray = true
- typ.T = FixedBytesTy
- typ.SliceSize = varSize
+ if parsedType[0] == "uint" || parsedType[0] == "int" {
+ // this should fail because it means that there's something wrong with
+ // the abi type (the compiler should always format it to the size...always)
+ return Type{}, fmt.Errorf("unsupported arg type: %s", t)
+ }
+ }
+ // varType is the parsed abi type
+ varType := parsedType[1]
+
+ switch varType {
+ case "int":
+ typ.Kind, typ.Type = reflectIntKindAndType(false, varSize)
+ typ.Size = varSize
+ typ.T = IntTy
+ case "uint":
+ typ.Kind, typ.Type = reflectIntKindAndType(true, varSize)
+ typ.Size = varSize
+ typ.T = UintTy
+ case "bool":
+ typ.Kind = reflect.Bool
+ typ.T = BoolTy
+ typ.Type = reflect.TypeOf(bool(false))
+ case "address":
+ typ.Kind = reflect.Array
+ typ.Type = address_t
+ typ.Size = 20
+ typ.T = AddressTy
+ case "string":
+ typ.Kind = reflect.String
+ typ.Type = reflect.TypeOf("")
+ typ.T = StringTy
+ case "bytes":
+ if varSize == 0 {
+ typ.T = BytesTy
+ typ.Kind = reflect.Slice
+ typ.Type = reflect.SliceOf(reflect.TypeOf(byte(0)))
+ } else {
+ typ.T = FixedBytesTy
+ typ.Kind = reflect.Array
+ typ.Size = varSize
+ typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
+ }
+ case "function":
+ typ.Kind = reflect.Array
+ typ.T = FunctionTy
+ typ.Size = 24
+ typ.Type = reflect.ArrayOf(24, reflect.TypeOf(byte(0)))
+ default:
+ return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
- case "function":
- sliceType, _ := NewType("uint8")
- typ.Elem = &sliceType
- typ.IsArray = true
- typ.T = FunctionTy
- typ.SliceSize = 24
- default:
- return Type{}, fmt.Errorf("unsupported arg type: %s", t)
}
return
@@ -183,7 +180,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
return nil, err
}
- if (t.IsSlice || t.IsArray) && t.T != BytesTy && t.T != FixedBytesTy && t.T != FunctionTy {
+ if t.T == SliceTy || t.T == ArrayTy {
var packed []byte
for i := 0; i < v.Len(); i++ {
@@ -193,18 +190,17 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
}
packed = append(packed, val...)
}
- if t.IsSlice {
+ if t.T == SliceTy {
return packBytesSlice(packed, v.Len()), nil
- } else if t.IsArray {
+ } else if t.T == ArrayTy {
return packed, nil
}
}
-
return packElement(t, v), nil
}
// requireLengthPrefix returns whether the type requires any sort of length
// prefixing.
func (t Type) requiresLengthPrefix() bool {
- return t.T != FixedBytesTy && (t.T == StringTy || t.T == BytesTy || t.IsSlice)
+ return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
}
diff --git a/accounts/abi/type_test.go b/accounts/abi/type_test.go
index 984a5bb4c..e55af1293 100644
--- a/accounts/abi/type_test.go
+++ b/accounts/abi/type_test.go
@@ -21,6 +21,7 @@ import (
"reflect"
"testing"
+ "github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common"
)
@@ -34,51 +35,58 @@ func TestTypeRegexp(t *testing.T) {
blob string
kind Type
}{
- {"bool", Type{Kind: reflect.Bool, T: BoolTy, stringKind: "bool"}},
- {"bool[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Bool, T: BoolTy, Elem: &Type{Kind: reflect.Bool, T: BoolTy, stringKind: "bool"}, stringKind: "bool[]"}},
- {"bool[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Bool, T: BoolTy, Elem: &Type{Kind: reflect.Bool, T: BoolTy, stringKind: "bool"}, stringKind: "bool[2]"}},
+ {"bool", Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}},
+ {"bool[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool(nil)), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}},
+ {"bool[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}},
+ {"bool[2][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
+ {"bool[][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
+ {"bool[][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
+ {"bool[2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
+ {"bool[2][][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][][2]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
+ {"bool[2][2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
+ {"bool[][][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
+ {"bool[][2][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
{"int8", Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}},
{"int16", Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}},
{"int32", Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}},
{"int64", Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}},
{"int256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}},
- {"int8[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
- {"int8[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
- {"int16[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
- {"int16[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
- {"int32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
- {"int32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
- {"int64[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
- {"int64[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
- {"int256[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
- {"int256[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
+ {"int8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}},
+ {"int8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8_t, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}},
+ {"int16[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}},
+ {"int16[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16_t, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}},
+ {"int32[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}},
+ {"int32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32_t, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}},
+ {"int64[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}},
+ {"int64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64_t, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}},
+ {"int256[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}},
+ {"int256[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
{"uint8", Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}},
{"uint16", Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}},
{"uint32", Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}},
{"uint64", Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}},
{"uint256", Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}},
- {"uint8[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
- {"uint8[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
- {"uint16[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
- {"uint16[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
- {"uint32[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
- {"uint32[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
- {"uint64[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
- {"uint64[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
- {"uint256[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
- {"uint256[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
- {"bytes32", Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}},
- {"bytes[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
- {"bytes[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsSlice: true, SliceSize: -1, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[2]"}},
- {"bytes32[]", Type{IsSlice: true, SliceSize: -1, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
- {"bytes32[2]", Type{IsArray: true, SliceSize: 2, Elem: &Type{IsArray: true, SliceSize: 32, Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, T: FixedBytesTy, stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
- {"string", Type{Kind: reflect.String, Size: -1, T: StringTy, stringKind: "string"}},
- {"string[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[]"}},
- {"string[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.String, T: StringTy, Size: -1, Elem: &Type{Kind: reflect.String, T: StringTy, Size: -1, stringKind: "string"}, stringKind: "string[2]"}},
+ {"uint8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}},
+ {"uint8[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8_t, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}},
+ {"uint16[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}},
+ {"uint16[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16_t, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}},
+ {"uint32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}},
+ {"uint32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32_t, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}},
+ {"uint64[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}},
+ {"uint64[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64_t, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}},
+ {"uint256[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}},
+ {"uint256[2]", Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: big_t, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}},
+ {"bytes32", Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
+ {"bytes[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
+ {"bytes[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}},
+ {"bytes32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][32]byte{}), Elem: &Type{Kind: reflect.Array, Type: reflect.TypeOf([32]byte{}), T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
+ {"bytes32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][32]byte{}), Elem: &Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
+ {"string", Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
+ {"string[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
+ {"string[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}},
{"address", Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}},
- {"address[]", Type{IsSlice: true, SliceSize: -1, Kind: reflect.Array, Type: address_t, T: AddressTy, Size: 20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
- {"address[2]", Type{IsArray: true, SliceSize: 2, Kind: reflect.Array, Type: address_t, T: AddressTy, Size: 20, Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
-
+ {"address[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}},
+ {"address[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: address_t, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
// TODO when fixed types are implemented properly
// {"fixed", Type{}},
// {"fixed128x128", Type{}},
@@ -87,13 +95,14 @@ func TestTypeRegexp(t *testing.T) {
// {"fixed128x128[]", Type{}},
// {"fixed128x128[2]", Type{}},
}
- for i, tt := range tests {
+
+ for _, tt := range tests {
typ, err := NewType(tt.blob)
if err != nil {
- t.Errorf("type %d: failed to parse type string: %v", i, err)
+ t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
}
if !reflect.DeepEqual(typ, tt.kind) {
- t.Errorf("type %d: parsed type mismatch:\n have %+v\n want %+v", i, typeWithoutStringer(typ), typeWithoutStringer(tt.kind))
+ t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", tt.blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(tt.kind)))
}
}
}
@@ -104,15 +113,90 @@ func TestTypeCheck(t *testing.T) {
input interface{}
err string
}{
- {"uint", big.NewInt(1), ""},
- {"int", big.NewInt(1), ""},
- {"uint30", big.NewInt(1), ""},
+ {"uint", big.NewInt(1), "unsupported arg type: uint"},
+ {"int", big.NewInt(1), "unsupported arg type: int"},
+ {"uint256", big.NewInt(1), ""},
+ {"uint256[][3][]", [][3][]*big.Int{{{}}}, ""},
+ {"uint256[][][3]", [3][][]*big.Int{{{}}}, ""},
+ {"uint256[3][][]", [][][3]*big.Int{{{}}}, ""},
+ {"uint256[3][3][3]", [3][3][3]*big.Int{{{}}}, ""},
+ {"uint8[][]", [][]uint8{}, ""},
+ {"int256", big.NewInt(1), ""},
+ {"uint8", uint8(1), ""},
+ {"uint16", uint16(1), ""},
+ {"uint32", uint32(1), ""},
+ {"uint64", uint64(1), ""},
+ {"int8", int8(1), ""},
+ {"int16", int16(1), ""},
+ {"int32", int32(1), ""},
+ {"int64", int64(1), ""},
+ {"uint24", big.NewInt(1), ""},
+ {"uint40", big.NewInt(1), ""},
+ {"uint48", big.NewInt(1), ""},
+ {"uint56", big.NewInt(1), ""},
+ {"uint72", big.NewInt(1), ""},
+ {"uint80", big.NewInt(1), ""},
+ {"uint88", big.NewInt(1), ""},
+ {"uint96", big.NewInt(1), ""},
+ {"uint104", big.NewInt(1), ""},
+ {"uint112", big.NewInt(1), ""},
+ {"uint120", big.NewInt(1), ""},
+ {"uint128", big.NewInt(1), ""},
+ {"uint136", big.NewInt(1), ""},
+ {"uint144", big.NewInt(1), ""},
+ {"uint152", big.NewInt(1), ""},
+ {"uint160", big.NewInt(1), ""},
+ {"uint168", big.NewInt(1), ""},
+ {"uint176", big.NewInt(1), ""},
+ {"uint184", big.NewInt(1), ""},
+ {"uint192", big.NewInt(1), ""},
+ {"uint200", big.NewInt(1), ""},
+ {"uint208", big.NewInt(1), ""},
+ {"uint216", big.NewInt(1), ""},
+ {"uint224", big.NewInt(1), ""},
+ {"uint232", big.NewInt(1), ""},
+ {"uint240", big.NewInt(1), ""},
+ {"uint248", big.NewInt(1), ""},
+ {"int24", big.NewInt(1), ""},
+ {"int40", big.NewInt(1), ""},
+ {"int48", big.NewInt(1), ""},
+ {"int56", big.NewInt(1), ""},
+ {"int72", big.NewInt(1), ""},
+ {"int80", big.NewInt(1), ""},
+ {"int88", big.NewInt(1), ""},
+ {"int96", big.NewInt(1), ""},
+ {"int104", big.NewInt(1), ""},
+ {"int112", big.NewInt(1), ""},
+ {"int120", big.NewInt(1), ""},
+ {"int128", big.NewInt(1), ""},
+ {"int136", big.NewInt(1), ""},
+ {"int144", big.NewInt(1), ""},
+ {"int152", big.NewInt(1), ""},
+ {"int160", big.NewInt(1), ""},
+ {"int168", big.NewInt(1), ""},
+ {"int176", big.NewInt(1), ""},
+ {"int184", big.NewInt(1), ""},
+ {"int192", big.NewInt(1), ""},
+ {"int200", big.NewInt(1), ""},
+ {"int208", big.NewInt(1), ""},
+ {"int216", big.NewInt(1), ""},
+ {"int224", big.NewInt(1), ""},
+ {"int232", big.NewInt(1), ""},
+ {"int240", big.NewInt(1), ""},
+ {"int248", big.NewInt(1), ""},
{"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"},
+ {"uint8", uint16(1), "abi: cannot use uint16 as type uint8 as argument"},
+ {"uint8", uint32(1), "abi: cannot use uint32 as type uint8 as argument"},
+ {"uint8", uint64(1), "abi: cannot use uint64 as type uint8 as argument"},
+ {"uint8", int8(1), "abi: cannot use int8 as type uint8 as argument"},
+ {"uint8", int16(1), "abi: cannot use int16 as type uint8 as argument"},
+ {"uint8", int32(1), "abi: cannot use int32 as type uint8 as argument"},
+ {"uint8", int64(1), "abi: cannot use int64 as type uint8 as argument"},
{"uint16", uint16(1), ""},
{"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
{"uint16[]", []uint16{1, 2, 3}, ""},
{"uint16[]", [3]uint16{1, 2, 3}, ""},
- {"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type []uint16 as argument"},
+ {"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type [0]uint16 as argument"},
{"uint16[3]", [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"},
{"uint16[3]", [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"uint16[3]", []uint16{1, 2, 3}, ""},
@@ -122,20 +206,61 @@ func TestTypeCheck(t *testing.T) {
{"address[1]", [1]common.Address{{1}}, ""},
{"address[2]", [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
{"bytes32", [32]byte{}, ""},
+ {"bytes31", [31]byte{}, ""},
+ {"bytes30", [30]byte{}, ""},
+ {"bytes29", [29]byte{}, ""},
+ {"bytes28", [28]byte{}, ""},
+ {"bytes27", [27]byte{}, ""},
+ {"bytes26", [26]byte{}, ""},
+ {"bytes25", [25]byte{}, ""},
+ {"bytes24", [24]byte{}, ""},
+ {"bytes23", [23]byte{}, ""},
+ {"bytes22", [22]byte{}, ""},
+ {"bytes21", [21]byte{}, ""},
+ {"bytes20", [20]byte{}, ""},
+ {"bytes19", [19]byte{}, ""},
+ {"bytes18", [18]byte{}, ""},
+ {"bytes17", [17]byte{}, ""},
+ {"bytes16", [16]byte{}, ""},
+ {"bytes15", [15]byte{}, ""},
+ {"bytes14", [14]byte{}, ""},
+ {"bytes13", [13]byte{}, ""},
+ {"bytes12", [12]byte{}, ""},
+ {"bytes11", [11]byte{}, ""},
+ {"bytes10", [10]byte{}, ""},
+ {"bytes9", [9]byte{}, ""},
+ {"bytes8", [8]byte{}, ""},
+ {"bytes7", [7]byte{}, ""},
+ {"bytes6", [6]byte{}, ""},
+ {"bytes5", [5]byte{}, ""},
+ {"bytes4", [4]byte{}, ""},
+ {"bytes3", [3]byte{}, ""},
+ {"bytes2", [2]byte{}, ""},
+ {"bytes1", [1]byte{}, ""},
{"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
{"bytes32", common.Hash{1}, ""},
- {"bytes31", [31]byte{}, ""},
+ {"bytes31", common.Hash{1}, "abi: cannot use common.Hash as type [31]uint8 as argument"},
{"bytes31", [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
{"bytes", []byte{0, 1}, ""},
- {"bytes", [2]byte{0, 1}, ""},
- {"bytes", common.Hash{1}, ""},
+ {"bytes", [2]byte{0, 1}, "abi: cannot use array as type slice as argument"},
+ {"bytes", common.Hash{1}, "abi: cannot use array as type slice as argument"},
{"string", "hello world", ""},
+ {"string", string(""), ""},
+ {"string", []byte{}, "abi: cannot use slice as type string as argument"},
{"bytes32[]", [][32]byte{{}}, ""},
{"function", [24]byte{}, ""},
+ {"bytes20", common.Address{}, ""},
+ {"address", [20]byte{}, ""},
+ {"address", common.Address{}, ""},
} {
typ, err := NewType(test.typ)
- if err != nil {
+ if err != nil && len(test.err) == 0 {
t.Fatal("unexpected parse error:", err)
+ } else if err != nil && len(test.err) != 0 {
+ if err.Error() != test.err {
+ t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
+ }
+ continue
}
err = typeCheck(typ, reflect.ValueOf(test.input))
diff --git a/accounts/abi/unpack.go b/accounts/abi/unpack.go
index fc41c88ac..57732797b 100644
--- a/accounts/abi/unpack.go
+++ b/accounts/abi/unpack.go
@@ -25,122 +25,20 @@ import (
"github.com/ethereum/go-ethereum/common"
)
-// toGoSliceType parses the input and casts it to the proper slice defined by the ABI
-// argument in T.
-func toGoSlice(i int, t Argument, output []byte) (interface{}, error) {
- index := i * 32
- // The slice must, at very least be large enough for the index+32 which is exactly the size required
- // for the [offset in output, size of offset].
- if index+32 > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), index+32)
- }
- elem := t.Type.Elem
-
- // first we need to create a slice of the type
- var refSlice reflect.Value
- switch elem.T {
- case IntTy, UintTy, BoolTy:
- // create a new reference slice matching the element type
- switch t.Type.Kind {
- case reflect.Bool:
- refSlice = reflect.ValueOf([]bool(nil))
- case reflect.Uint8:
- refSlice = reflect.ValueOf([]uint8(nil))
- case reflect.Uint16:
- refSlice = reflect.ValueOf([]uint16(nil))
- case reflect.Uint32:
- refSlice = reflect.ValueOf([]uint32(nil))
- case reflect.Uint64:
- refSlice = reflect.ValueOf([]uint64(nil))
- case reflect.Int8:
- refSlice = reflect.ValueOf([]int8(nil))
- case reflect.Int16:
- refSlice = reflect.ValueOf([]int16(nil))
- case reflect.Int32:
- refSlice = reflect.ValueOf([]int32(nil))
- case reflect.Int64:
- refSlice = reflect.ValueOf([]int64(nil))
- default:
- refSlice = reflect.ValueOf([]*big.Int(nil))
- }
- case AddressTy: // address must be of slice Address
- refSlice = reflect.ValueOf([]common.Address(nil))
- case HashTy: // hash must be of slice hash
- refSlice = reflect.ValueOf([]common.Hash(nil))
- case FixedBytesTy:
- refSlice = reflect.ValueOf([][]byte(nil))
- default: // no other types are supported
- return nil, fmt.Errorf("abi: unsupported slice type %v", elem.T)
- }
-
- var slice []byte
- var size int
- var offset int
- if t.Type.IsSlice {
- // get the offset which determines the start of this array ...
- offset = int(binary.BigEndian.Uint64(output[index+24 : index+32]))
- if offset+32 > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
- }
-
- slice = output[offset:]
- // ... starting with the size of the array in elements ...
- size = int(binary.BigEndian.Uint64(slice[24:32]))
- slice = slice[32:]
- // ... and make sure that we've at the very least the amount of bytes
- // available in the buffer.
- if size*32 > len(slice) {
- return nil, fmt.Errorf("abi: cannot marshal in to go slice: insufficient size output %d require %d", len(output), offset+32+size*32)
- }
-
- // reslice to match the required size
- slice = slice[:size*32]
- } else if t.Type.IsArray {
- //get the number of elements in the array
- size = t.Type.SliceSize
-
- //check to make sure array size matches up
- if index+32*size > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), index+32*size)
- }
- //slice is there for a fixed amount of times
- slice = output[index : index+size*32]
- }
-
- for i := 0; i < size; i++ {
- var (
- inter interface{} // interface type
- returnOutput = slice[i*32 : i*32+32] // the return output
- err error
- )
- // set inter to the correct type (cast)
- switch elem.T {
- case IntTy, UintTy:
- inter = readInteger(t.Type.Kind, returnOutput)
- case BoolTy:
- inter, err = readBool(returnOutput)
- if err != nil {
- return nil, err
- }
- case AddressTy:
- inter = common.BytesToAddress(returnOutput)
- case HashTy:
- inter = common.BytesToHash(returnOutput)
- case FixedBytesTy:
- inter = returnOutput
- }
- // append the item to our reflect slice
- refSlice = reflect.Append(refSlice, reflect.ValueOf(inter))
- }
-
- // return the interface
- return refSlice.Interface(), nil
+// unpacker is a utility interface that enables us to have
+// abstraction between events and methods and also to properly
+// "unpack" them; e.g. events use Inputs, methods use Outputs.
+type unpacker interface {
+ tupleUnpack(v interface{}, output []byte) error
+ singleUnpack(v interface{}, output []byte) error
+ isTupleReturn() bool
}
+// reads the integer based on its kind
func readInteger(kind reflect.Kind, b []byte) interface{} {
switch kind {
case reflect.Uint8:
- return uint8(b[len(b)-1])
+ return b[len(b)-1]
case reflect.Uint16:
return binary.BigEndian.Uint16(b[len(b)-2:])
case reflect.Uint32:
@@ -160,13 +58,10 @@ func readInteger(kind reflect.Kind, b []byte) interface{} {
}
}
+// reads a bool
func readBool(word []byte) (bool, error) {
- if len(word) != 32 {
- return false, fmt.Errorf("abi: fatal error: incorrect word length")
- }
-
- for i, b := range word {
- if b != 0 && i != 31 {
+ for _, b := range word[:31] {
+ if b != 0 {
return false, errBadBool
}
}
@@ -178,58 +73,144 @@ func readBool(word []byte) (bool, error) {
default:
return false, errBadBool
}
+}
+// A function type is simply the address with the function selection signature at the end.
+// This enforces that standard by always presenting it as a 24-array (address + sig = 24 bytes)
+func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
+ if t.T != FunctionTy {
+ return [24]byte{}, fmt.Errorf("abi: invalid type in call to make function type byte array.")
+ }
+ if garbage := binary.BigEndian.Uint64(word[24:32]); garbage != 0 {
+ err = fmt.Errorf("abi: got improperly encoded function type, got %v", word)
+ } else {
+ copy(funcTy[:], word[0:24])
+ }
+ return
}
-// toGoType parses the input and casts it to the proper type defined by the ABI
-// argument in T.
-func toGoType(i int, t Argument, output []byte) (interface{}, error) {
- // we need to treat slices differently
- if (t.Type.IsSlice || t.Type.IsArray) && t.Type.T != BytesTy && t.Type.T != StringTy && t.Type.T != FixedBytesTy && t.Type.T != FunctionTy {
- return toGoSlice(i, t, output)
+// through reflection, creates a fixed array to be read from
+func readFixedBytes(t Type, word []byte) (interface{}, error) {
+ if t.T != FixedBytesTy {
+ return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array.")
}
+ // convert
+ array := reflect.New(t.Type).Elem()
- index := i * 32
- if index+32 > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
+ reflect.Copy(array, reflect.ValueOf(word[0:t.Size]))
+ return array.Interface(), nil
+
+}
+
+// iteratively unpack elements
+func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
+ if start+32*size > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go array: offset %d would go over slice boundary (len=%d)", len(output), start+32*size)
}
- // Parse the given index output and check whether we need to read
- // a different offset and length based on the type (i.e. string, bytes)
- var returnOutput []byte
- switch t.Type.T {
- case StringTy, BytesTy: // variable arrays are written at the end of the return bytes
- // parse offset from which we should start reading
- offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
- if offset+32 > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32)
+ // this value will become our slice or our array, depending on the type
+ var refSlice reflect.Value
+ slice := output[start : start+size*32]
+
+ if t.T == SliceTy {
+ // declare our slice
+ refSlice = reflect.MakeSlice(t.Type, size, size)
+ } else if t.T == ArrayTy {
+ // declare our array
+ refSlice = reflect.New(t.Type).Elem()
+ } else {
+ return nil, fmt.Errorf("abi: invalid type in array/slice unpacking stage")
+ }
+
+ for i, j := start, 0; j*32 < len(slice); i, j = i+32, j+1 {
+ // this corrects the arrangement so that we get all the underlying array values
+ if t.Elem.T == ArrayTy && j != 0 {
+ i = start + t.Elem.Size*32*j
}
- // parse the size up until we should be reading
- size := int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
- if offset+32+size > len(output) {
- return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+size)
+ inter, err := toGoType(i, *t.Elem, output)
+ if err != nil {
+ return nil, err
}
+ // append the item to our reflect slice
+ refSlice.Index(j).Set(reflect.ValueOf(inter))
+ }
- // get the bytes for this return value
- returnOutput = output[offset+32 : offset+32+size]
- default:
+ // return the interface
+ return refSlice.Interface(), nil
+}
+
+// toGoType parses the output bytes and recursively assigns the value of these bytes
+// into a go type with accordance with the ABI spec.
+func toGoType(index int, t Type, output []byte) (interface{}, error) {
+ if index+32 > len(output) {
+ return nil, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), index+32)
+ }
+
+ var (
+ returnOutput []byte
+ begin, end int
+ err error
+ )
+
+ // if we require a length prefix, find the beginning word and size returned.
+ if t.requiresLengthPrefix() {
+ begin, end, err = lengthPrefixPointsTo(index, output)
+ if err != nil {
+ return nil, err
+ }
+ } else {
returnOutput = output[index : index+32]
}
- // convert the bytes to whatever is specified by the ABI.
- switch t.Type.T {
+ switch t.T {
+ case SliceTy:
+ return forEachUnpack(t, output, begin, end)
+ case ArrayTy:
+ return forEachUnpack(t, output, index, t.Size)
+ case StringTy: // variable arrays are written at the end of the return bytes
+ return string(output[begin : begin+end]), nil
case IntTy, UintTy:
- return readInteger(t.Type.Kind, returnOutput), nil
+ return readInteger(t.Kind, returnOutput), nil
case BoolTy:
return readBool(returnOutput)
case AddressTy:
return common.BytesToAddress(returnOutput), nil
case HashTy:
return common.BytesToHash(returnOutput), nil
- case BytesTy, FixedBytesTy, FunctionTy:
- return returnOutput, nil
- case StringTy:
- return string(returnOutput), nil
+ case BytesTy:
+ return output[begin : begin+end], nil
+ case FixedBytesTy:
+ return readFixedBytes(t, returnOutput)
+ case FunctionTy:
+ return readFunctionType(t, returnOutput)
+ default:
+ return nil, fmt.Errorf("abi: unknown type %v", t.T)
+ }
+}
+
+// interprets a 32 byte slice as an offset and then determines which indice to look to decode the type.
+func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err error) {
+ offset := int(binary.BigEndian.Uint64(output[index+24 : index+32]))
+ if offset+32 > len(output) {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %d would go over slice boundary (len=%d)", len(output), offset+32)
+ }
+ length = int(binary.BigEndian.Uint64(output[offset+24 : offset+32]))
+ if offset+32+length > len(output) {
+ return 0, 0, fmt.Errorf("abi: cannot marshal in to go type: length insufficient %d require %d", len(output), offset+32+length)
+ }
+ start = offset + 32
+
+ //fmt.Printf("LENGTH PREFIX INFO: \nsize: %v\noffset: %v\nstart: %v\n", length, offset, start)
+ return
+}
+
+// checks for proper formatting of byte output
+func bytesAreProper(output []byte) error {
+ if len(output) == 0 {
+ return fmt.Errorf("abi: unmarshalling empty output")
+ } else if len(output)%32 != 0 {
+ return fmt.Errorf("abi: improperly formatted output")
+ } else {
+ return nil
}
- return nil, fmt.Errorf("abi: unknown type %v", t.Type.T)
}
diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go
index 8e3afee4e..1e21aafc0 100644
--- a/accounts/abi/unpack_test.go
+++ b/accounts/abi/unpack_test.go
@@ -18,6 +18,7 @@ package abi
import (
"bytes"
+ "encoding/hex"
"fmt"
"math/big"
"reflect"
@@ -27,263 +28,261 @@ import (
"github.com/ethereum/go-ethereum/common"
)
-func TestSimpleMethodUnpack(t *testing.T) {
- for i, test := range []struct {
- def string // definition of the **output** ABI params
- marshalledOutput []byte // evm return data
- expectedOut interface{} // the expected output
- outVar string // the output variable (e.g. uint32, *big.Int, etc)
- err string // empty or error if expected
- }{
- {
- `[ { "type": "bool" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- bool(true),
- "bool",
- "",
- },
- {
- `[ { "type": "uint32" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- uint32(1),
- "uint32",
- "",
- },
- {
- `[ { "type": "uint32" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- nil,
- "uint16",
- "abi: cannot unmarshal uint32 in to uint16",
- },
- {
- `[ { "type": "uint17" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- nil,
- "uint16",
- "abi: cannot unmarshal *big.Int in to uint16",
- },
- {
- `[ { "type": "uint17" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- big.NewInt(1),
- "*big.Int",
- "",
- },
-
- {
- `[ { "type": "int32" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- int32(1),
- "int32",
- "",
- },
- {
- `[ { "type": "int32" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- nil,
- "int16",
- "abi: cannot unmarshal int32 in to int16",
- },
- {
- `[ { "type": "int17" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- nil,
- "int16",
- "abi: cannot unmarshal *big.Int in to int16",
- },
- {
- `[ { "type": "int17" } ]`,
- common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"),
- big.NewInt(1),
- "*big.Int",
- "",
- },
-
- {
- `[ { "type": "address" } ]`,
- common.Hex2Bytes("0000000000000000000000000100000000000000000000000000000000000000"),
- common.Address{1},
- "address",
- "",
- },
- {
- `[ { "type": "bytes32" } ]`,
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- "bytes",
- "",
- },
- {
- `[ { "type": "bytes32" } ]`,
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- "hash",
- "",
- },
- {
- `[ { "type": "bytes32" } ]`,
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- "interface",
- "",
- },
- {
- `[ { "type": "function" } ]`,
- common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
- [24]byte{1},
- "function",
- "",
- },
- } {
- abiDefinition := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
- abi, err := JSON(strings.NewReader(abiDefinition))
- if err != nil {
- t.Errorf("%d failed. %v", i, err)
- continue
- }
+type unpackTest struct {
+ def string // ABI definition JSON
+ enc string // evm return data
+ want interface{} // the expected output
+ err string // empty or error if expected
+}
- var outvar interface{}
- switch test.outVar {
- case "bool":
- var v bool
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "uint8":
- var v uint8
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "uint16":
- var v uint16
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "uint32":
- var v uint32
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "uint64":
- var v uint64
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "int8":
- var v int8
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "int16":
- var v int16
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "int32":
- var v int32
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "int64":
- var v int64
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "*big.Int":
- var v *big.Int
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "address":
- var v common.Address
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "bytes":
- var v []byte
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "hash":
- var v common.Hash
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v.Bytes()[:]
- case "function":
- var v [24]byte
- err = abi.Unpack(&v, "method", test.marshalledOutput)
- outvar = v
- case "interface":
- err = abi.Unpack(&outvar, "method", test.marshalledOutput)
- default:
- t.Errorf("unsupported type '%v' please add it to the switch statement in this test", test.outVar)
- continue
+func (test unpackTest) checkError(err error) error {
+ if err != nil {
+ if len(test.err) == 0 {
+ return fmt.Errorf("expected no err but got: %v", err)
+ } else if err.Error() != test.err {
+ return fmt.Errorf("expected err: '%v' got err: %q", test.err, err)
}
+ } else if len(test.err) > 0 {
+ return fmt.Errorf("expected err: %v but got none", test.err)
+ }
+ return nil
+}
- if err != nil && len(test.err) == 0 {
- t.Errorf("%d failed. Expected no err but got: %v", i, err)
- continue
+var unpackTests = []unpackTest{
+ {
+ def: `[{ "type": "bool" }]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: true,
+ },
+ {
+ def: `[{"type": "uint32"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: uint32(1),
+ },
+ {
+ def: `[{"type": "uint32"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: uint16(0),
+ err: "abi: cannot unmarshal uint32 in to uint16",
+ },
+ {
+ def: `[{"type": "uint17"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: uint16(0),
+ err: "abi: cannot unmarshal *big.Int in to uint16",
+ },
+ {
+ def: `[{"type": "uint17"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: big.NewInt(1),
+ },
+ {
+ def: `[{"type": "int32"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: int32(1),
+ },
+ {
+ def: `[{"type": "int32"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: int16(0),
+ err: "abi: cannot unmarshal int32 in to int16",
+ },
+ {
+ def: `[{"type": "int17"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: int16(0),
+ err: "abi: cannot unmarshal *big.Int in to int16",
+ },
+ {
+ def: `[{"type": "int17"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001",
+ want: big.NewInt(1),
+ },
+ {
+ def: `[{"type": "address"}]`,
+ enc: "0000000000000000000000000100000000000000000000000000000000000000",
+ want: common.Address{1},
+ },
+ {
+ def: `[{"type": "bytes32"}]`,
+ enc: "0100000000000000000000000000000000000000000000000000000000000000",
+ want: [32]byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+ },
+ {
+ def: `[{"type": "bytes"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
+ want: common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
+ },
+ {
+ def: `[{"type": "bytes"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
+ want: [32]byte{},
+ err: "abi: cannot unmarshal []uint8 in to [32]uint8",
+ },
+ {
+ def: `[{"type": "bytes32"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200100000000000000000000000000000000000000000000000000000000000000",
+ want: []byte(nil),
+ err: "abi: cannot unmarshal [32]uint8 in to []uint8",
+ },
+ {
+ def: `[{"type": "bytes32"}]`,
+ enc: "0100000000000000000000000000000000000000000000000000000000000000",
+ want: common.HexToHash("0100000000000000000000000000000000000000000000000000000000000000"),
+ },
+ {
+ def: `[{"type": "function"}]`,
+ enc: "0100000000000000000000000000000000000000000000000000000000000000",
+ want: [24]byte{1},
+ },
+ // slices
+ {
+ def: `[{"type": "uint8[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []uint8{1, 2},
+ },
+ {
+ def: `[{"type": "uint8[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]uint8{1, 2},
+ },
+ // multi dimensional, if these pass, all types that don't require length prefix should pass
+ {
+ def: `[{"type": "uint8[][]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [][]uint8{{1, 2}, {1, 2}},
+ },
+ {
+ def: `[{"type": "uint8[2][2]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2][2]uint8{{1, 2}, {1, 2}},
+ },
+ {
+ def: `[{"type": "uint8[][2]"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
+ want: [2][]uint8{{1}, {1}},
+ },
+ {
+ def: `[{"type": "uint8[2][]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [][2]uint8{{1, 2}},
+ },
+ {
+ def: `[{"type": "uint16[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []uint16{1, 2},
+ },
+ {
+ def: `[{"type": "uint16[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]uint16{1, 2},
+ },
+ {
+ def: `[{"type": "uint32[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []uint32{1, 2},
+ },
+ {
+ def: `[{"type": "uint32[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]uint32{1, 2},
+ },
+ {
+ def: `[{"type": "uint64[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []uint64{1, 2},
+ },
+ {
+ def: `[{"type": "uint64[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]uint64{1, 2},
+ },
+ {
+ def: `[{"type": "uint256[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []*big.Int{big.NewInt(1), big.NewInt(2)},
+ },
+ {
+ def: `[{"type": "uint256[3]"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
+ want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
+ },
+ {
+ def: `[{"type": "int8[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []int8{1, 2},
+ },
+ {
+ def: `[{"type": "int8[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]int8{1, 2},
+ },
+ {
+ def: `[{"type": "int16[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []int16{1, 2},
+ },
+ {
+ def: `[{"type": "int16[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]int16{1, 2},
+ },
+ {
+ def: `[{"type": "int32[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []int32{1, 2},
+ },
+ {
+ def: `[{"type": "int32[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]int32{1, 2},
+ },
+ {
+ def: `[{"type": "int64[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []int64{1, 2},
+ },
+ {
+ def: `[{"type": "int64[2]"}]`,
+ enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: [2]int64{1, 2},
+ },
+ {
+ def: `[{"type": "int256[]"}]`,
+ enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
+ want: []*big.Int{big.NewInt(1), big.NewInt(2)},
+ },
+ {
+ def: `[{"type": "int256[3]"}]`,
+ enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
+ want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
+ },
+}
+
+func TestUnpack(t *testing.T) {
+ for i, test := range unpackTests {
+ def := fmt.Sprintf(`[{ "name" : "method", "outputs": %s}]`, test.def)
+ abi, err := JSON(strings.NewReader(def))
+ if err != nil {
+ t.Fatalf("invalid ABI definition %s: %v", def, err)
}
- if err == nil && len(test.err) != 0 {
- t.Errorf("%d failed. Expected err: %v but got none", i, test.err)
- continue
+ encb, err := hex.DecodeString(test.enc)
+ if err != nil {
+ t.Fatalf("invalid hex: %s" + test.enc)
}
- if err != nil && len(test.err) != 0 && err.Error() != test.err {
- t.Errorf("%d failed. Expected err: '%v' got err: '%v'", i, test.err, err)
+ outptr := reflect.New(reflect.TypeOf(test.want))
+ err = abi.Unpack(outptr.Interface(), "method", encb)
+ if err := test.checkError(err); err != nil {
+ t.Errorf("test %d (%v) failed: %v", i, test.def, err)
continue
}
-
- if err == nil {
- if !reflect.DeepEqual(test.expectedOut, outvar) {
- t.Errorf("%d failed. Output error: expected %v, got %v", i, test.expectedOut, outvar)
- }
+ out := outptr.Elem().Interface()
+ if !reflect.DeepEqual(test.want, out) {
+ t.Errorf("test %d (%v) failed: expected %v, got %v", i, test.def, test.want, out)
}
}
}
-func TestUnpackSetInterfaceSlice(t *testing.T) {
- var (
- var1 = new(uint8)
- var2 = new(uint8)
- )
- out := []interface{}{var1, var2}
- abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint8"}, {"type":"uint8"}]}]`))
- if err != nil {
- t.Fatal(err)
- }
- marshalledReturn := append(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")...)
- err = abi.Unpack(&out, "ints", marshalledReturn)
- if err != nil {
- t.Fatal(err)
- }
- if *var1 != 1 {
- t.Error("expected var1 to be 1, got", *var1)
- }
- if *var2 != 2 {
- t.Error("expected var2 to be 2, got", *var2)
- }
-
- out = []interface{}{var1}
- err = abi.Unpack(&out, "ints", marshalledReturn)
-
- expErr := "abi: cannot marshal in to slices of unequal size (require: 2, got: 1)"
- if err == nil || err.Error() != expErr {
- t.Error("expected err:", expErr, "Got:", err)
- }
-}
-
-func TestUnpackSetInterfaceArrayOutput(t *testing.T) {
- var (
- var1 = new([1]uint32)
- var2 = new([1]uint32)
- )
- out := []interface{}{var1, var2}
- abi, err := JSON(strings.NewReader(`[{"type":"function", "name":"ints", "outputs":[{"type":"uint32[1]"}, {"type":"uint32[1]"}]}]`))
- if err != nil {
- t.Fatal(err)
- }
- marshalledReturn := append(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")...)
- err = abi.Unpack(&out, "ints", marshalledReturn)
- if err != nil {
- t.Fatal(err)
- }
-
- if *var1 != [1]uint32{1} {
- t.Error("expected var1 to be [1], got", *var1)
- }
- if *var2 != [1]uint32{2} {
- t.Error("expected var2 to be [2], got", *var2)
- }
-}
-
func TestMultiReturnWithStruct(t *testing.T) {
const definition = `[
{ "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
@@ -337,101 +336,6 @@ func TestMultiReturnWithStruct(t *testing.T) {
}
}
-func TestMultiReturnWithSlice(t *testing.T) {
- const definition = `[
- { "name" : "multi", "constant" : false, "outputs": [ { "name": "Int", "type": "uint256" }, { "name": "String", "type": "string" } ] }]`
-
- abi, err := JSON(strings.NewReader(definition))
- if err != nil {
- t.Fatal(err)
- }
-
- // using buff to make the code readable
- buff := new(bytes.Buffer)
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000005"))
- stringOut := "hello"
- buff.Write(common.RightPadBytes([]byte(stringOut), 32))
-
- var inter []interface{}
- err = abi.Unpack(&inter, "multi", buff.Bytes())
- if err != nil {
- t.Error(err)
- }
-
- if len(inter) != 2 {
- t.Fatal("expected 2 results got", len(inter))
- }
-
- if num, ok := inter[0].(*big.Int); !ok || num.Cmp(big.NewInt(1)) != 0 {
- t.Error("expected index 0 to be 1 got", num)
- }
-
- if str, ok := inter[1].(string); !ok || str != stringOut {
- t.Error("expected index 1 to be", stringOut, "got", str)
- }
-}
-
-func TestMarshalArrays(t *testing.T) {
- const definition = `[
- { "name" : "bytes32", "constant" : false, "outputs": [ { "type": "bytes32" } ] },
- { "name" : "bytes10", "constant" : false, "outputs": [ { "type": "bytes10" } ] }
- ]`
-
- abi, err := JSON(strings.NewReader(definition))
- if err != nil {
- t.Fatal(err)
- }
-
- output := common.LeftPadBytes([]byte{1}, 32)
-
- var bytes10 [10]byte
- err = abi.Unpack(&bytes10, "bytes32", output)
- if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
- t.Error("expected error or bytes32 not be assignable to bytes10:", err)
- }
-
- var bytes32 [32]byte
- err = abi.Unpack(&bytes32, "bytes32", output)
- if err != nil {
- t.Error("didn't expect error:", err)
- }
- if !bytes.Equal(bytes32[:], output) {
- t.Error("expected bytes32[31] to be 1 got", bytes32[31])
- }
-
- type (
- B10 [10]byte
- B32 [32]byte
- )
-
- var b10 B10
- err = abi.Unpack(&b10, "bytes32", output)
- if err == nil || err.Error() != "abi: cannot unmarshal src (len=32) in to dst (len=10)" {
- t.Error("expected error or bytes32 not be assignable to bytes10:", err)
- }
-
- var b32 B32
- err = abi.Unpack(&b32, "bytes32", output)
- if err != nil {
- t.Error("didn't expect error:", err)
- }
- if !bytes.Equal(b32[:], output) {
- t.Error("expected bytes32[31] to be 1 got", bytes32[31])
- }
-
- output[10] = 1
- var shortAssignLong [32]byte
- err = abi.Unpack(&shortAssignLong, "bytes10", output)
- if err != nil {
- t.Error("didn't expect error:", err)
- }
- if !bytes.Equal(output, shortAssignLong[:]) {
- t.Errorf("expected %x to be %x", shortAssignLong, output)
- }
-}
-
func TestUnmarshal(t *testing.T) {
const definition = `[
{ "name" : "int", "constant" : false, "outputs": [ { "type": "uint256" } ] },
@@ -450,6 +354,29 @@ func TestUnmarshal(t *testing.T) {
}
buff := new(bytes.Buffer)
+ // marshall mixed bytes (mixedBytes)
+ p0, p0Exp := []byte{}, common.Hex2Bytes("01020000000000000000")
+ p1, p1Exp := [32]byte{}, common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000ddeeff")
+ mixedBytes := []interface{}{&p0, &p1}
+
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
+ buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000ddeeff"))
+ buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000a"))
+ buff.Write(common.Hex2Bytes("0102000000000000000000000000000000000000000000000000000000000000"))
+
+ err = abi.Unpack(&mixedBytes, "mixedBytes", buff.Bytes())
+ if err != nil {
+ t.Error(err)
+ } else {
+ if !bytes.Equal(p0, p0Exp) {
+ t.Errorf("unexpected value unpacked: want %x, got %x", p0Exp, p0)
+ }
+
+ if !bytes.Equal(p1[:], p1Exp) {
+ t.Errorf("unexpected value unpacked: want %x, got %x", p1Exp, p1)
+ }
+ }
+
// marshal int
var Int *big.Int
err = abi.Unpack(&Int, "int", common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
@@ -473,6 +400,7 @@ func TestUnmarshal(t *testing.T) {
}
// marshal dynamic bytes max length 32
+ buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
bytesOut := common.RightPadBytes([]byte("hello"), 32)
@@ -504,11 +432,11 @@ func TestUnmarshal(t *testing.T) {
t.Errorf("expected %x got %x", bytesOut, Bytes)
}
- // marshall dynamic bytes max length 63
+ // marshall dynamic bytes max length 64
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000003f"))
- bytesOut = common.RightPadBytes([]byte("hello"), 63)
+ bytesOut = common.RightPadBytes([]byte("hello"), 64)
buff.Write(bytesOut)
err = abi.Unpack(&Bytes, "bytes", buff.Bytes())
@@ -516,8 +444,8 @@ func TestUnmarshal(t *testing.T) {
t.Error(err)
}
- if !bytes.Equal(Bytes, bytesOut) {
- t.Errorf("expected %x got %x", bytesOut, Bytes)
+ if !bytes.Equal(Bytes, bytesOut[:len(bytesOut)-1]) {
+ t.Errorf("expected %x got %x", bytesOut[:len(bytesOut)-1], Bytes)
}
// marshal dynamic bytes output empty
@@ -569,29 +497,6 @@ func TestUnmarshal(t *testing.T) {
t.Error("expected error")
}
- // marshal mixed bytes
- buff.Reset()
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040"))
- fixed := common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")
- buff.Write(fixed)
- buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020"))
- bytesOut = common.RightPadBytes([]byte("hello"), 32)
- buff.Write(bytesOut)
-
- var out []interface{}
- err = abi.Unpack(&out, "mixedBytes", buff.Bytes())
- if err != nil {
- t.Fatal("didn't expect error:", err)
- }
-
- if !bytes.Equal(bytesOut, out[0].([]byte)) {
- t.Errorf("expected %x, got %x", bytesOut, out[0])
- }
-
- if !bytes.Equal(fixed, out[1].([]byte)) {
- t.Errorf("expected %x, got %x", fixed, out[1])
- }
-
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001"))
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"))
diff --git a/accounts/keystore/account_cache.go b/accounts/keystore/account_cache.go
index 4b08cc202..71f698ece 100644
--- a/accounts/keystore/account_cache.go
+++ b/accounts/keystore/account_cache.go
@@ -20,7 +20,6 @@ import (
"bufio"
"encoding/json"
"fmt"
- "io/ioutil"
"os"
"path/filepath"
"sort"
@@ -75,13 +74,6 @@ type accountCache struct {
fileC fileCache
}
-// fileCache is a cache of files seen during scan of keystore
-type fileCache struct {
- all *set.SetNonTS // list of all files
- mtime time.Time // latest mtime seen
- mu sync.RWMutex
-}
-
func newAccountCache(keydir string) (*accountCache, chan struct{}) {
ac := &accountCache{
keydir: keydir,
@@ -236,66 +228,22 @@ func (ac *accountCache) close() {
ac.mu.Unlock()
}
-// scanFiles performs a new scan on the given directory, compares against the already
-// cached filenames, and returns file sets: new, missing , modified
-func (fc *fileCache) scanFiles(keyDir string) (set.Interface, set.Interface, set.Interface, error) {
- t0 := time.Now()
- files, err := ioutil.ReadDir(keyDir)
- t1 := time.Now()
- if err != nil {
- return nil, nil, nil, err
- }
- fc.mu.RLock()
- prevMtime := fc.mtime
- fc.mu.RUnlock()
-
- filesNow := set.NewNonTS()
- moddedFiles := set.NewNonTS()
- var newMtime time.Time
- for _, fi := range files {
- modTime := fi.ModTime()
- path := filepath.Join(keyDir, fi.Name())
- if skipKeyFile(fi) {
- log.Trace("Ignoring file on account scan", "path", path)
- continue
- }
- filesNow.Add(path)
- if modTime.After(prevMtime) {
- moddedFiles.Add(path)
- }
- if modTime.After(newMtime) {
- newMtime = modTime
- }
- }
- t2 := time.Now()
-
- fc.mu.Lock()
- // Missing = previous - current
- missing := set.Difference(fc.all, filesNow)
- // New = current - previous
- newFiles := set.Difference(filesNow, fc.all)
- // Modified = modified - new
- modified := set.Difference(moddedFiles, newFiles)
- fc.all = filesNow
- fc.mtime = newMtime
- fc.mu.Unlock()
- t3 := time.Now()
- log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
- return newFiles, missing, modified, nil
-}
-
// scanAccounts checks if any changes have occurred on the filesystem, and
// updates the account cache accordingly
func (ac *accountCache) scanAccounts() error {
- newFiles, missingFiles, modified, err := ac.fileC.scanFiles(ac.keydir)
- t1 := time.Now()
+ // Scan the entire folder metadata for file changes
+ creates, deletes, updates, err := ac.fileC.scan(ac.keydir)
if err != nil {
log.Debug("Failed to reload keystore contents", "err", err)
return err
}
+ if creates.Size() == 0 && deletes.Size() == 0 && updates.Size() == 0 {
+ return nil
+ }
+ // Create a helper method to scan the contents of the key files
var (
- buf = new(bufio.Reader)
- keyJSON struct {
+ buf = new(bufio.Reader)
+ key struct {
Address string `json:"address"`
}
)
@@ -308,9 +256,9 @@ func (ac *accountCache) scanAccounts() error {
defer fd.Close()
buf.Reset(fd)
// Parse the address.
- keyJSON.Address = ""
- err = json.NewDecoder(buf).Decode(&keyJSON)
- addr := common.HexToAddress(keyJSON.Address)
+ key.Address = ""
+ err = json.NewDecoder(buf).Decode(&key)
+ addr := common.HexToAddress(key.Address)
switch {
case err != nil:
log.Debug("Failed to decode keystore key", "path", path, "err", err)
@@ -321,47 +269,30 @@ func (ac *accountCache) scanAccounts() error {
}
return nil
}
+ // Process all the file diffs
+ start := time.Now()
- for _, p := range newFiles.List() {
- path, _ := p.(string)
- a := readAccount(path)
- if a != nil {
+ for _, p := range creates.List() {
+ if a := readAccount(p.(string)); a != nil {
ac.add(*a)
}
}
- for _, p := range missingFiles.List() {
- path, _ := p.(string)
- ac.deleteByFile(path)
+ for _, p := range deletes.List() {
+ ac.deleteByFile(p.(string))
}
-
- for _, p := range modified.List() {
- path, _ := p.(string)
- a := readAccount(path)
+ for _, p := range updates.List() {
+ path := p.(string)
ac.deleteByFile(path)
- if a != nil {
+ if a := readAccount(path); a != nil {
ac.add(*a)
}
}
-
- t2 := time.Now()
+ end := time.Now()
select {
case ac.notify <- struct{}{}:
default:
}
- log.Trace("Handled keystore changes", "time", t2.Sub(t1))
-
+ log.Trace("Handled keystore changes", "time", end.Sub(start))
return nil
}
-
-func skipKeyFile(fi os.FileInfo) bool {
- // Skip editor backups and UNIX-style hidden files.
- if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
- return true
- }
- // Skip misc special files, directories (yes, symlinks too).
- if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
- return true
- }
- return false
-}
diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go
index e3dc31065..fe9233c04 100644
--- a/accounts/keystore/account_cache_test.go
+++ b/accounts/keystore/account_cache_test.go
@@ -59,7 +59,7 @@ func TestWatchNewFile(t *testing.T) {
// Ensure the watcher is started before adding any files.
ks.Accounts()
- time.Sleep(200 * time.Millisecond)
+ time.Sleep(1000 * time.Millisecond)
// Move in the files.
wantAccounts := make([]accounts.Account, len(cachetestAccounts))
@@ -349,6 +349,9 @@ func TestUpdatedKeyfileContents(t *testing.T) {
return
}
+ // needed so that modTime of `file` is different to its current value after forceCopyFile
+ time.Sleep(1000 * time.Millisecond)
+
// Now replace file contents
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
t.Fatal(err)
@@ -362,6 +365,9 @@ func TestUpdatedKeyfileContents(t *testing.T) {
return
}
+ // needed so that modTime of `file` is different to its current value after forceCopyFile
+ time.Sleep(1000 * time.Millisecond)
+
// Now replace file contents again
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
t.Fatal(err)
@@ -374,6 +380,10 @@ func TestUpdatedKeyfileContents(t *testing.T) {
t.Error(err)
return
}
+
+ // needed so that modTime of `file` is different to its current value after ioutil.WriteFile
+ time.Sleep(1000 * time.Millisecond)
+
// Now replace file contents with crap
if err := ioutil.WriteFile(file, []byte("foo"), 0644); err != nil {
t.Fatal(err)
diff --git a/accounts/keystore/file_cache.go b/accounts/keystore/file_cache.go
new file mode 100644
index 000000000..c91b7b7b6
--- /dev/null
+++ b/accounts/keystore/file_cache.go
@@ -0,0 +1,102 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library 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.
+//
+// The go-ethereum library 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 Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package keystore
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/log"
+ set "gopkg.in/fatih/set.v0"
+)
+
+// fileCache is a cache of files seen during scan of keystore.
+type fileCache struct {
+ all *set.SetNonTS // Set of all files from the keystore folder
+ lastMod time.Time // Last time instance when a file was modified
+ mu sync.RWMutex
+}
+
+// scan performs a new scan on the given directory, compares against the already
+// cached filenames, and returns file sets: creates, deletes, updates.
+func (fc *fileCache) scan(keyDir string) (set.Interface, set.Interface, set.Interface, error) {
+ t0 := time.Now()
+
+ // List all the failes from the keystore folder
+ files, err := ioutil.ReadDir(keyDir)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ t1 := time.Now()
+
+ fc.mu.Lock()
+ defer fc.mu.Unlock()
+
+ // Iterate all the files and gather their metadata
+ all := set.NewNonTS()
+ mods := set.NewNonTS()
+
+ var newLastMod time.Time
+ for _, fi := range files {
+ // Skip any non-key files from the folder
+ path := filepath.Join(keyDir, fi.Name())
+ if skipKeyFile(fi) {
+ log.Trace("Ignoring file on account scan", "path", path)
+ continue
+ }
+ // Gather the set of all and fresly modified files
+ all.Add(path)
+
+ modified := fi.ModTime()
+ if modified.After(fc.lastMod) {
+ mods.Add(path)
+ }
+ if modified.After(newLastMod) {
+ newLastMod = modified
+ }
+ }
+ t2 := time.Now()
+
+ // Update the tracked files and return the three sets
+ deletes := set.Difference(fc.all, all) // Deletes = previous - current
+ creates := set.Difference(all, fc.all) // Creates = current - previous
+ updates := set.Difference(mods, creates) // Updates = modified - creates
+
+ fc.all, fc.lastMod = all, newLastMod
+ t3 := time.Now()
+
+ // Report on the scanning stats and return
+ log.Debug("FS scan times", "list", t1.Sub(t0), "set", t2.Sub(t1), "diff", t3.Sub(t2))
+ return creates, deletes, updates, nil
+}
+
+// skipKeyFile ignores editor backups, hidden files and folders/symlinks.
+func skipKeyFile(fi os.FileInfo) bool {
+ // Skip editor backups and UNIX-style hidden files.
+ if strings.HasSuffix(fi.Name(), "~") || strings.HasPrefix(fi.Name(), ".") {
+ return true
+ }
+ // Skip misc special files, directories (yes, symlinks too).
+ if fi.IsDir() || fi.Mode()&os.ModeType != 0 {
+ return true
+ }
+ return false
+}
diff --git a/accounts/keystore/keystore_passphrase.go b/accounts/keystore/keystore_passphrase.go
index 535608a60..eaec39f7d 100644
--- a/accounts/keystore/keystore_passphrase.go
+++ b/accounts/keystore/keystore_passphrase.go
@@ -28,6 +28,7 @@ package keystore
import (
"bytes"
"crypto/aes"
+ crand "crypto/rand"
"crypto/sha256"
"encoding/hex"
"encoding/json"
@@ -90,6 +91,12 @@ func (ks keyStorePassphrase) GetKey(addr common.Address, filename, auth string)
return key, nil
}
+// StoreKey generates a key, encrypts with 'auth' and stores in the given directory
+func StoreKey(dir, auth string, scryptN, scryptP int) (common.Address, error) {
+ _, a, err := storeNewKey(&keyStorePassphrase{dir, scryptN, scryptP}, crand.Reader, auth)
+ return a.Address, err
+}
+
func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) error {
keyjson, err := EncryptKey(key, auth, ks.scryptN, ks.scryptP)
if err != nil {
diff --git a/accounts/keystore/presale.go b/accounts/keystore/presale.go
index ed900ad08..1554294e1 100644
--- a/accounts/keystore/presale.go
+++ b/accounts/keystore/presale.go
@@ -58,6 +58,9 @@ func decryptPreSaleKey(fileContent []byte, password string) (key *Key, err error
if err != nil {
return nil, errors.New("invalid hex in encSeed")
}
+ if len(encSeedBytes) < 16 {
+ return nil, errors.New("invalid encSeed, too short")
+ }
iv := encSeedBytes[:16]
cipherText := encSeedBytes[16:]
/*
diff --git a/accounts/keystore/watch.go b/accounts/keystore/watch.go
index 602300b10..bbcfb9925 100644
--- a/accounts/keystore/watch.go
+++ b/accounts/keystore/watch.go
@@ -81,10 +81,14 @@ func (w *watcher) loop() {
// When an event occurs, the reload call is delayed a bit so that
// multiple events arriving quickly only cause a single reload.
var (
- debounce = time.NewTimer(0)
debounceDuration = 500 * time.Millisecond
rescanTriggered = false
+ debounce = time.NewTimer(0)
)
+ // Ignore initial trigger
+ if !debounce.Stop() {
+ <-debounce.C
+ }
defer debounce.Stop()
for {
select {
diff --git a/accounts/manager.go b/accounts/manager.go
index 78ddb1368..96ca298fc 100644
--- a/accounts/manager.go
+++ b/accounts/manager.go
@@ -41,6 +41,11 @@ type Manager struct {
// NewManager creates a generic account manager to sign transaction via various
// supported backends.
func NewManager(backends ...Backend) *Manager {
+ // Retrieve the initial list of wallets from the backends and sort by URL
+ var wallets []Wallet
+ for _, backend := range backends {
+ wallets = merge(wallets, backend.Wallets()...)
+ }
// Subscribe to wallet notifications from all backends
updates := make(chan WalletEvent, 4*len(backends))
@@ -48,11 +53,6 @@ func NewManager(backends ...Backend) *Manager {
for i, backend := range backends {
subs[i] = backend.Subscribe(updates)
}
- // Retrieve the initial list of wallets from the backends and sort by URL
- var wallets []Wallet
- for _, backend := range backends {
- wallets = merge(wallets, backend.Wallets()...)
- }
// Assemble the account manager and return
am := &Manager{
backends: make(map[reflect.Type][]Backend),