This commit is contained in:
Lucas Hendren 2019-01-26 12:45:25 -05:00
commit e5f630b649
448 changed files with 25871 additions and 11569 deletions

27
.github/CODEOWNERS vendored
View file

@ -2,6 +2,7 @@
# Each line is a file pattern followed by one or more owners. # Each line is a file pattern followed by one or more owners.
accounts/usbwallet @karalabe accounts/usbwallet @karalabe
accounts/abi @gballet
consensus @karalabe consensus @karalabe
core/ @karalabe @holiman core/ @karalabe @holiman
eth/ @karalabe eth/ @karalabe
@ -9,27 +10,7 @@ les/ @zsfelfoldi
light/ @zsfelfoldi light/ @zsfelfoldi
mobile/ @karalabe mobile/ @karalabe
p2p/ @fjl @zsfelfoldi p2p/ @fjl @zsfelfoldi
p2p/simulations @lmars p2p/simulations @zelig @nonsense @janos @justelad
p2p/protocols @zelig p2p/protocols @zelig @nonsense @janos @justelad
swarm/api/http @justelad p2p/testing @zelig @nonsense @janos @justelad
swarm/bmt @zelig
swarm/dev @lmars
swarm/fuse @jmozah @holisticode
swarm/grafana_dashboards @nonsense
swarm/metrics @nonsense @holisticode
swarm/multihash @nolash
swarm/network/bitvector @zelig @janos
swarm/network/priorityqueue @zelig @janos
swarm/network/simulations @zelig @janos
swarm/network/stream @janos @zelig @holisticode @justelad
swarm/network/stream/intervals @janos
swarm/network/stream/testing @zelig
swarm/pot @zelig
swarm/pss @nolash @zelig @nonsense
swarm/services @zelig
swarm/state @justelad
swarm/storage/encryption @zelig @nagydani
swarm/storage/mock @janos
swarm/storage/feed @nolash @jpeletier
swarm/testutil @lmars
whisper/ @gballet @gluk256 whisper/ @gballet @gluk256

View file

@ -1,7 +1,7 @@
# Number of days of inactivity before an Issue is closed for lack of response # Number of days of inactivity before an Issue is closed for lack of response
daysUntilClose: 30 daysUntilClose: 30
# Label requiring a response # Label requiring a response
responseRequiredLabel: more-information-needed responseRequiredLabel: "need:more-information"
# Comment to post when closing an Issue for lack of response. Set to `false` to disable # Comment to post when closing an Issue for lack of response. Set to `false` to disable
closeComment: > closeComment: >
This issue has been automatically closed because there has been no response This issue has been automatically closed because there has been no response

2
.github/stale.yml vendored
View file

@ -7,7 +7,7 @@ exemptLabels:
- pinned - pinned
- security - security
# Label to use when marking an issue as stale # Label to use when marking an issue as stale
staleLabel: stale staleLabel: "status:inactive"
# Comment to post when marking an issue as stale. Set to `false` to disable # Comment to post when marking an issue as stale. Set to `false` to disable
markComment: > markComment: >
This issue has been automatically marked as stale because it has not had This issue has been automatically marked as stale because it has not had

View file

@ -29,6 +29,14 @@ matrix:
- os: osx - os: osx
go: 1.11.x go: 1.11.x
script: script:
- echo "Increase the maximum number of open file descriptors on macOS"
- NOFILE=20480
- sudo sysctl -w kern.maxfiles=$NOFILE
- sudo sysctl -w kern.maxfilesperproc=$NOFILE
- sudo launchctl limit maxfiles $NOFILE $NOFILE
- sudo launchctl limit maxfiles
- ulimit -S -n $NOFILE
- ulimit -n
- unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703 - unset -f cd # workaround for https://github.com/travis-ci/travis-ci/issues/8703
- go run build/ci.go install - go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES - go run build/ci.go test -coverage $TEST_PACKAGES
@ -148,7 +156,7 @@ matrix:
git: git:
submodules: false # avoid cloning ethereum/tests submodules: false # avoid cloning ethereum/tests
before_install: before_install:
- curl https://storage.googleapis.com/golang/go1.11.2.linux-amd64.tar.gz | tar -xz - curl https://storage.googleapis.com/golang/go1.11.4.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH - export PATH=`pwd`/go/bin:$PATH
- export GOROOT=`pwd`/go - export GOROOT=`pwd`/go
- export GOPATH=$HOME/go - export GOPATH=$HOME/go

View file

@ -18,7 +18,7 @@ For prerequisites and detailed build instructions please read the
[Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum) [Installation Instructions](https://github.com/ethereum/go-ethereum/wiki/Building-Ethereum)
on the wiki. on the wiki.
Building geth requires both a Go (version 1.7 or later) and a C compiler. Building geth requires both a Go (version 1.9 or later) and a C compiler.
You can install them using your favourite package manager. You can install them using your favourite package manager.
Once the dependencies are installed, run Once the dependencies are installed, run
@ -168,7 +168,7 @@ HTTP based JSON-RPC API options:
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it) * `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to connect You'll need to use your own programming environments' capabilities (libraries, tools, etc) to connect
via HTTP, WS or IPC to a Geth node configured with the above flags and you'll need to speak [JSON-RPC](http://www.jsonrpc.org/specification) via HTTP, WS or IPC to a Geth node configured with the above flags and you'll need to speak [JSON-RPC](https://www.jsonrpc.org/specification)
on all transports. You can reuse the same connection for multiple requests! on all transports. You can reuse the same connection for multiple requests!
**Note: Please understand the security implications of opening up an HTTP/WS based transport before **Note: Please understand the security implications of opening up an HTTP/WS based transport before

View file

@ -58,13 +58,11 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
return nil, err return nil, err
} }
return arguments, nil return arguments, nil
} }
method, exist := abi.Methods[name] method, exist := abi.Methods[name]
if !exist { if !exist {
return nil, fmt.Errorf("method '%s' not found", name) return nil, fmt.Errorf("method '%s' not found", name)
} }
arguments, err := method.Inputs.Pack(args...) arguments, err := method.Inputs.Pack(args...)
if err != nil { if err != nil {
return nil, err return nil, err
@ -82,7 +80,7 @@ func (abi ABI) Unpack(v interface{}, name string, output []byte) (err error) {
// we need to decide whether we're calling a method or an event // we need to decide whether we're calling a method or an event
if method, ok := abi.Methods[name]; ok { if method, ok := abi.Methods[name]; ok {
if len(output)%32 != 0 { if len(output)%32 != 0 {
return fmt.Errorf("abi: improperly formatted output") return fmt.Errorf("abi: improperly formatted output: %s - Bytes: [%+v]", string(output), output)
} }
return method.Outputs.Unpack(v, output) return method.Outputs.Unpack(v, output)
} else if event, ok := abi.Events[name]; ok { } else if event, ok := abi.Events[name]; ok {

View file

@ -22,11 +22,10 @@ import (
"fmt" "fmt"
"log" "log"
"math/big" "math/big"
"reflect"
"strings" "strings"
"testing" "testing"
"reflect"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
) )
@ -52,11 +51,14 @@ const jsondata2 = `
{ "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] }, { "type" : "function", "name" : "slice", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint32[2]" } ] },
{ "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] }, { "type" : "function", "name" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
{ "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] }, { "type" : "function", "name" : "sliceAddress", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "address[]" } ] },
{ "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] } { "type" : "function", "name" : "sliceMultiAddress", "constant" : false, "inputs" : [ { "name" : "a", "type" : "address[]" }, { "name" : "b", "type" : "address[]" } ] },
{ "type" : "function", "name" : "nestedArray", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint256[2][2]" }, { "name" : "b", "type" : "address[]" } ] },
{ "type" : "function", "name" : "nestedArray2", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][2]" } ] },
{ "type" : "function", "name" : "nestedSlice", "constant" : false, "inputs" : [ { "name" : "a", "type" : "uint8[][]" } ] }
]` ]`
func TestReader(t *testing.T) { func TestReader(t *testing.T) {
Uint256, _ := NewType("uint256") Uint256, _ := NewType("uint256", nil)
exp := ABI{ exp := ABI{
Methods: map[string]Method{ Methods: map[string]Method{
"balance": { "balance": {
@ -177,7 +179,7 @@ func TestTestSlice(t *testing.T) {
} }
func TestMethodSignature(t *testing.T) { func TestMethodSignature(t *testing.T) {
String, _ := NewType("string") String, _ := NewType("string", nil)
m := Method{"foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil} m := Method{"foo", false, []Argument{{"bar", String, false}, {"baz", String, false}}, nil}
exp := "foo(string,string)" exp := "foo(string,string)"
if m.Sig() != exp { if m.Sig() != exp {
@ -189,12 +191,31 @@ func TestMethodSignature(t *testing.T) {
t.Errorf("expected ids to match %x != %x", m.Id(), idexp) t.Errorf("expected ids to match %x != %x", m.Id(), idexp)
} }
uintt, _ := NewType("uint256") uintt, _ := NewType("uint256", nil)
m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil} m = Method{"foo", false, []Argument{{"bar", uintt, false}}, nil}
exp = "foo(uint256)" exp = "foo(uint256)"
if m.Sig() != exp { if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig()) t.Error("signature mismatch", exp, "!=", m.Sig())
} }
// Method with tuple arguments
s, _ := NewType("tuple", []ArgumentMarshaling{
{Name: "a", Type: "int256"},
{Name: "b", Type: "int256[]"},
{Name: "c", Type: "tuple[]", Components: []ArgumentMarshaling{
{Name: "x", Type: "int256"},
{Name: "y", Type: "int256"},
}},
{Name: "d", Type: "tuple[2]", Components: []ArgumentMarshaling{
{Name: "x", Type: "int256"},
{Name: "y", Type: "int256"},
}},
})
m = Method{"foo", false, []Argument{{"s", s, false}, {"bar", String, false}}, nil}
exp = "foo((int256,int256[],(int256,int256)[],(int256,int256)[2]),string)"
if m.Sig() != exp {
t.Error("signature mismatch", exp, "!=", m.Sig())
}
} }
func TestMultiPack(t *testing.T) { func TestMultiPack(t *testing.T) {
@ -564,11 +585,13 @@ func TestBareEvents(t *testing.T) {
const definition = `[ const definition = `[
{ "type" : "event", "name" : "balance" }, { "type" : "event", "name" : "balance" },
{ "type" : "event", "name" : "anon", "anonymous" : true}, { "type" : "event", "name" : "anon", "anonymous" : true},
{ "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] } { "type" : "event", "name" : "args", "inputs" : [{ "indexed":false, "name":"arg0", "type":"uint256" }, { "indexed":true, "name":"arg1", "type":"address" }] },
{ "type" : "event", "name" : "tuple", "inputs" : [{ "indexed":false, "name":"t", "type":"tuple", "components":[{"name":"a", "type":"uint256"}] }, { "indexed":true, "name":"arg1", "type":"address" }] }
]` ]`
arg0, _ := NewType("uint256") arg0, _ := NewType("uint256", nil)
arg1, _ := NewType("address") arg1, _ := NewType("address", nil)
tuple, _ := NewType("tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
expectedEvents := map[string]struct { expectedEvents := map[string]struct {
Anonymous bool Anonymous bool
@ -580,6 +603,10 @@ func TestBareEvents(t *testing.T) {
{Name: "arg0", Type: arg0, Indexed: false}, {Name: "arg0", Type: arg0, Indexed: false},
{Name: "arg1", Type: arg1, Indexed: true}, {Name: "arg1", Type: arg1, Indexed: true},
}}, }},
"tuple": {false, []Argument{
{Name: "t", Type: tuple, Indexed: false},
{Name: "arg1", Type: arg1, Indexed: true},
}},
} }
abi, err := JSON(strings.NewReader(definition)) abi, err := JSON(strings.NewReader(definition))
@ -646,7 +673,7 @@ func TestUnpackEvent(t *testing.T) {
} }
type ReceivedEvent struct { type ReceivedEvent struct {
Address common.Address Sender common.Address
Amount *big.Int Amount *big.Int
Memo []byte Memo []byte
} }
@ -655,19 +682,15 @@ func TestUnpackEvent(t *testing.T) {
err = abi.Unpack(&ev, "received", data) err = abi.Unpack(&ev, "received", data)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else {
t.Logf("len(data): %d; received event: %+v", len(data), ev)
} }
type ReceivedAddrEvent struct { type ReceivedAddrEvent struct {
Address common.Address Sender common.Address
} }
var receivedAddrEv ReceivedAddrEvent var receivedAddrEv ReceivedAddrEvent
err = abi.Unpack(&receivedAddrEv, "receivedAddr", data) err = abi.Unpack(&receivedAddrEv, "receivedAddr", data)
if err != nil { if err != nil {
t.Error(err) t.Error(err)
} else {
t.Logf("len(data): %d; received event: %+v", len(data), receivedAddrEv)
} }
} }

View file

@ -33,24 +33,27 @@ type Argument struct {
type Arguments []Argument type Arguments []Argument
// UnmarshalJSON implements json.Unmarshaler interface type ArgumentMarshaling struct {
func (argument *Argument) UnmarshalJSON(data []byte) error {
var extarg struct {
Name string Name string
Type string Type string
Components []ArgumentMarshaling
Indexed bool Indexed bool
} }
err := json.Unmarshal(data, &extarg)
// UnmarshalJSON implements json.Unmarshaler interface
func (argument *Argument) UnmarshalJSON(data []byte) error {
var arg ArgumentMarshaling
err := json.Unmarshal(data, &arg)
if err != nil { if err != nil {
return fmt.Errorf("argument json err: %v", err) return fmt.Errorf("argument json err: %v", err)
} }
argument.Type, err = NewType(extarg.Type) argument.Type, err = NewType(arg.Type, arg.Components)
if err != nil { if err != nil {
return err return err
} }
argument.Name = extarg.Name argument.Name = arg.Name
argument.Indexed = extarg.Indexed argument.Indexed = arg.Indexed
return nil return nil
} }
@ -85,7 +88,6 @@ func (arguments Arguments) isTuple() bool {
// Unpack performs the operation hexdata -> Go format // Unpack performs the operation hexdata -> Go format
func (arguments Arguments) Unpack(v interface{}, data []byte) error { func (arguments Arguments) Unpack(v interface{}, data []byte) error {
// make sure the passed value is arguments pointer // make sure the passed value is arguments pointer
if reflect.Ptr != reflect.ValueOf(v).Kind() { if reflect.Ptr != reflect.ValueOf(v).Kind() {
return fmt.Errorf("abi: Unpack(non-pointer %T)", v) return fmt.Errorf("abi: Unpack(non-pointer %T)", v)
@ -97,52 +99,134 @@ func (arguments Arguments) Unpack(v interface{}, data []byte) error {
if arguments.isTuple() { if arguments.isTuple() {
return arguments.unpackTuple(v, marshalledValues) return arguments.unpackTuple(v, marshalledValues)
} }
return arguments.unpackAtomic(v, marshalledValues) return arguments.unpackAtomic(v, marshalledValues[0])
} }
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error { // unpack sets the unmarshalled value to go format.
// Note the dst here must be settable.
func unpack(t *Type, dst interface{}, src interface{}) error {
var (
dstVal = reflect.ValueOf(dst).Elem()
srcVal = reflect.ValueOf(src)
)
if t.T != TupleTy && !((t.T == SliceTy || t.T == ArrayTy) && t.Elem.T == TupleTy) {
return set(dstVal, srcVal)
}
switch t.T {
case TupleTy:
if dstVal.Kind() != reflect.Struct {
return fmt.Errorf("abi: invalid dst value for unpack, want struct, got %s", dstVal.Kind())
}
fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, dstVal)
if err != nil {
return err
}
for i, elem := range t.TupleElems {
fname := fieldmap[t.TupleRawNames[i]]
field := dstVal.FieldByName(fname)
if !field.IsValid() {
return fmt.Errorf("abi: field %s can't found in the given value", t.TupleRawNames[i])
}
if err := unpack(elem, field.Addr().Interface(), srcVal.Field(i).Interface()); err != nil {
return err
}
}
return nil
case SliceTy:
if dstVal.Kind() != reflect.Slice {
return fmt.Errorf("abi: invalid dst value for unpack, want slice, got %s", dstVal.Kind())
}
slice := reflect.MakeSlice(dstVal.Type(), srcVal.Len(), srcVal.Len())
for i := 0; i < slice.Len(); i++ {
if err := unpack(t.Elem, slice.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
return err
}
}
dstVal.Set(slice)
case ArrayTy:
if dstVal.Kind() != reflect.Array {
return fmt.Errorf("abi: invalid dst value for unpack, want array, got %s", dstVal.Kind())
}
array := reflect.New(dstVal.Type()).Elem()
for i := 0; i < array.Len(); i++ {
if err := unpack(t.Elem, array.Index(i).Addr().Interface(), srcVal.Index(i).Interface()); err != nil {
return err
}
}
dstVal.Set(array)
}
return nil
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues interface{}) error {
if arguments.LengthNonIndexed() == 0 {
return nil
}
argument := arguments.NonIndexed()[0]
elem := reflect.ValueOf(v).Elem()
if elem.Kind() == reflect.Struct {
fieldmap, err := mapArgNamesToStructFields([]string{argument.Name}, elem)
if err != nil {
return err
}
field := elem.FieldByName(fieldmap[argument.Name])
if !field.IsValid() {
return fmt.Errorf("abi: field %s can't be found in the given value", argument.Name)
}
return unpack(&argument.Type, field.Addr().Interface(), marshalledValues)
}
return unpack(&argument.Type, elem.Addr().Interface(), marshalledValues)
}
// unpackTuple unpacks ( hexdata -> go ) a batch of values.
func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interface{}) error {
var ( var (
value = reflect.ValueOf(v).Elem() value = reflect.ValueOf(v).Elem()
typ = value.Type() typ = value.Type()
kind = value.Kind() kind = value.Kind()
) )
if err := requireUnpackKind(value, typ, kind, arguments); err != nil { if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
return err return err
} }
// If the interface is a struct, get of abi->struct_field mapping // If the interface is a struct, get of abi->struct_field mapping
var abi2struct map[string]string var abi2struct map[string]string
if kind == reflect.Struct { if kind == reflect.Struct {
var err error var (
abi2struct, err = mapAbiToStructFields(arguments, value) argNames []string
err error
)
for _, arg := range arguments.NonIndexed() {
argNames = append(argNames, arg.Name)
}
abi2struct, err = mapArgNamesToStructFields(argNames, value)
if err != nil { if err != nil {
return err return err
} }
} }
for i, arg := range arguments.NonIndexed() { for i, arg := range arguments.NonIndexed() {
reflectValue := reflect.ValueOf(marshalledValues[i])
switch kind { switch kind {
case reflect.Struct: case reflect.Struct:
if structField, ok := abi2struct[arg.Name]; ok { field := value.FieldByName(abi2struct[arg.Name])
if err := set(value.FieldByName(structField), reflectValue, arg); err != nil { if !field.IsValid() {
return err return fmt.Errorf("abi: field %s can't be found in the given value", arg.Name)
} }
if err := unpack(&arg.Type, field.Addr().Interface(), marshalledValues[i]); err != nil {
return err
} }
case reflect.Slice, reflect.Array: case reflect.Slice, reflect.Array:
if value.Len() < i { if value.Len() < i {
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len()) return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
} }
v := value.Index(i) v := value.Index(i)
if err := requireAssignable(v, reflectValue); err != nil { if err := requireAssignable(v, reflect.ValueOf(marshalledValues[i])); err != nil {
return err return err
} }
if err := unpack(&arg.Type, v.Addr().Interface(), marshalledValues[i]); err != nil {
if err := set(v.Elem(), reflectValue, arg); err != nil {
return err return err
} }
default: default:
@ -150,48 +234,7 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
} }
} }
return nil return nil
}
// unpackAtomic unpacks ( hexdata -> go ) a single value
func (arguments Arguments) unpackAtomic(v interface{}, marshalledValues []interface{}) error {
if len(marshalledValues) != 1 {
return fmt.Errorf("abi: wrong length, expected single value, got %d", len(marshalledValues))
}
elem := reflect.ValueOf(v).Elem()
kind := elem.Kind()
reflectValue := reflect.ValueOf(marshalledValues[0])
var abi2struct map[string]string
if kind == reflect.Struct {
var err error
if abi2struct, err = mapAbiToStructFields(arguments, elem); err != nil {
return err
}
arg := arguments.NonIndexed()[0]
if structField, ok := abi2struct[arg.Name]; ok {
return set(elem.FieldByName(structField), reflectValue, arg)
}
return nil
}
return set(elem, reflectValue, arguments.NonIndexed()[0])
}
// Computes the full size of an array;
// i.e. counting nested arrays, which count towards size for unpacking.
func getArraySize(arr *Type) int {
size := arr.Size
// Arrays can be nested, with each element being the same size
arr = arr.Elem
for arr.T == ArrayTy {
// Keep multiplying by elem.Size while the elem is an array.
size *= arr.Size
arr = arr.Elem
}
// Now we have the full array size, including its children.
return size
} }
// UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification, // UnpackValues can be used to unpack ABI-encoded hexdata according to the ABI-specification,
@ -202,7 +245,7 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
virtualArgs := 0 virtualArgs := 0
for index, arg := range arguments.NonIndexed() { for index, arg := range arguments.NonIndexed() {
marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data) marshalledValue, err := toGoType((index+virtualArgs)*32, arg.Type, data)
if arg.Type.T == ArrayTy { if arg.Type.T == ArrayTy && !isDynamicType(arg.Type) {
// If we have a static array, like [3]uint256, these are coded as // If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256. // just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when // This means that we need to add two 'virtual' arguments when
@ -213,7 +256,11 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
// //
// Calculate the full array size to get the correct offset for the next argument. // Calculate the full array size to get the correct offset for the next argument.
// Decrement it by 1, as the normal index increment is still applied. // Decrement it by 1, as the normal index increment is still applied.
virtualArgs += getArraySize(&arg.Type) - 1 virtualArgs += getTypeSize(arg.Type)/32 - 1
} else if arg.Type.T == TupleTy && !isDynamicType(arg.Type) {
// If we have a static tuple, like (uint256, bool, uint256), these are
// coded as just like uint256,bool,uint256
virtualArgs += getTypeSize(arg.Type)/32 - 1
} }
if err != nil { if err != nil {
return nil, err return nil, err
@ -243,11 +290,7 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
// input offset is the bytes offset for packed output // input offset is the bytes offset for packed output
inputOffset := 0 inputOffset := 0
for _, abiArg := range abiArgs { for _, abiArg := range abiArgs {
if abiArg.Type.T == ArrayTy { inputOffset += getTypeSize(abiArg.Type)
inputOffset += 32 * abiArg.Type.Size
} else {
inputOffset += 32
}
} }
var ret []byte var ret []byte
for i, a := range args { for i, a := range args {
@ -257,14 +300,13 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
// check for a slice type (string, bytes, slice) // check for dynamic types
if input.Type.requiresLengthPrefix() { if isDynamicType(input.Type) {
// calculate the offset
offset := inputOffset + len(variableInput)
// set the offset // set the offset
ret = append(ret, packNum(reflect.ValueOf(offset))...) ret = append(ret, packNum(reflect.ValueOf(inputOffset))...)
// Append the packed output to the variable input. The variable input // calculate next offset
// will be appended at the end of the input. inputOffset += len(packed)
// append to variable input
variableInput = append(variableInput, packed...) variableInput = append(variableInput, packed...)
} else { } else {
// append the packed value to the input // append the packed value to the input
@ -277,14 +319,13 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
return ret, nil return ret, nil
} }
// capitalise makes the first character of a string upper case, also removing any // ToCamelCase converts an under-score string to a camel-case string
// prefixing underscores from the variable names. func ToCamelCase(input string) string {
func capitalise(input string) string { parts := strings.Split(input, "_")
for len(input) > 0 && input[0] == '_' { for i, s := range parts {
input = input[1:] if len(s) > 0 {
parts[i] = strings.ToUpper(s[:1]) + s[1:]
} }
if len(input) == 0 {
return ""
} }
return strings.ToUpper(input[:1]) + input[1:] return strings.Join(parts, "")
} }

View file

@ -38,7 +38,7 @@ type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Tra
type CallOpts struct { type CallOpts struct {
Pending bool // Whether to operate on the pending state or the last known one Pending bool // Whether to operate on the pending state or the last known one
From common.Address // Optional the sender address, otherwise the first account is used From common.Address // Optional the sender address, otherwise the first account is used
BlockNumber *big.Int // Optional the block number on which the call should be performed
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout) Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
} }
@ -148,10 +148,10 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
} }
} }
} else { } else {
output, err = c.caller.CallContract(ctx, msg, nil) output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
if err == nil && len(output) == 0 { if err == nil && len(output) == 0 {
// Make sure we have a contract to operate on, and bail out otherwise. // Make sure we have a contract to operate on, and bail out otherwise.
if code, err = c.caller.CodeAt(ctx, c.address, nil); err != nil { if code, err = c.caller.CodeAt(ctx, c.address, opts.BlockNumber); err != nil {
return err return err
} else if len(code) == 0 { } else if len(code) == 0 {
return ErrNoCode return ErrNoCode

View file

@ -0,0 +1,64 @@
package bind_test
import (
"context"
"math/big"
"testing"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
)
type mockCaller struct {
codeAtBlockNumber *big.Int
callContractBlockNumber *big.Int
}
func (mc *mockCaller) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
mc.codeAtBlockNumber = blockNumber
return []byte{1, 2, 3}, nil
}
func (mc *mockCaller) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
mc.callContractBlockNumber = blockNumber
return nil, nil
}
func TestPassingBlockNumber(t *testing.T) {
mc := &mockCaller{}
bc := bind.NewBoundContract(common.HexToAddress("0x0"), abi.ABI{
Methods: map[string]abi.Method{
"something": {
Name: "something",
Outputs: abi.Arguments{},
},
},
}, mc, nil, nil)
var ret string
blockNumber := big.NewInt(42)
bc.Call(&bind.CallOpts{BlockNumber: blockNumber}, &ret, "something")
if mc.callContractBlockNumber != blockNumber {
t.Fatalf("CallContract() was not passed the block number")
}
if mc.codeAtBlockNumber != blockNumber {
t.Fatalf("CodeAt() was not passed the block number")
}
bc.Call(&bind.CallOpts{}, &ret, "something")
if mc.callContractBlockNumber != nil {
t.Fatalf("CallContract() was passed a block number when it should not have been")
}
if mc.codeAtBlockNumber != nil {
t.Fatalf("CodeAt() was passed a block number when it should not have been")
}
}

View file

@ -381,54 +381,23 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
// methodNormalizer is a name transformer that modifies Solidity method names to // methodNormalizer is a name transformer that modifies Solidity method names to
// conform to target language naming concentions. // conform to target language naming concentions.
var methodNormalizer = map[Lang]func(string) string{ var methodNormalizer = map[Lang]func(string) string{
LangGo: capitalise, LangGo: abi.ToCamelCase,
LangJava: decapitalise, LangJava: decapitalise,
} }
// capitalise makes a camel-case string which starts with an upper case character. // capitalise makes a camel-case string which starts with an upper case character.
func capitalise(input string) string { func capitalise(input string) string {
for len(input) > 0 && input[0] == '_' { return abi.ToCamelCase(input)
input = input[1:]
}
if len(input) == 0 {
return ""
}
return toCamelCase(strings.ToUpper(input[:1]) + input[1:])
} }
// decapitalise makes a camel-case string which starts with a lower case character. // decapitalise makes a camel-case string which starts with a lower case character.
func decapitalise(input string) string { func decapitalise(input string) string {
for len(input) > 0 && input[0] == '_' {
input = input[1:]
}
if len(input) == 0 { if len(input) == 0 {
return "" return input
}
return toCamelCase(strings.ToLower(input[:1]) + input[1:])
} }
// toCamelCase converts an under-score string to a camel-case string goForm := abi.ToCamelCase(input)
func toCamelCase(input string) string { return strings.ToLower(goForm[:1]) + goForm[1:]
toupper := false
result := ""
for k, v := range input {
switch {
case k == 0:
result = strings.ToUpper(string(input[0]))
case toupper:
result += strings.ToUpper(string(v))
toupper = false
case v == '_':
toupper = true
default:
result += string(v)
}
}
return result
} }
// structured checks whether a list of ABI data types has enough information to // structured checks whether a list of ABI data types has enough information to

View file

@ -36,12 +36,12 @@ type Event struct {
func (e Event) String() string { func (e Event) String() string {
inputs := make([]string, len(e.Inputs)) inputs := make([]string, len(e.Inputs))
for i, input := range e.Inputs { for i, input := range e.Inputs {
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type) inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
if input.Indexed { if input.Indexed {
inputs[i] = fmt.Sprintf("%v indexed %v", input.Name, input.Type) inputs[i] = fmt.Sprintf("%v indexed %v", input.Type, input.Name)
} }
} }
return fmt.Sprintf("e %v(%v)", e.Name, strings.Join(inputs, ", ")) return fmt.Sprintf("event %v(%v)", e.Name, strings.Join(inputs, ", "))
} }
// Id returns the canonical representation of the event's signature used by the // Id returns the canonical representation of the event's signature used by the

View file

@ -87,12 +87,12 @@ func TestEventId(t *testing.T) {
}{ }{
{ {
definition: `[ definition: `[
{ "type" : "event", "name" : "balance", "inputs": [{ "name" : "in", "type": "uint256" }] }, { "type" : "event", "name" : "Balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
{ "type" : "event", "name" : "check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] } { "type" : "event", "name" : "Check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] }
]`, ]`,
expectations: map[string]common.Hash{ expectations: map[string]common.Hash{
"balance": crypto.Keccak256Hash([]byte("balance(uint256)")), "Balance": crypto.Keccak256Hash([]byte("Balance(uint256)")),
"check": crypto.Keccak256Hash([]byte("check(address,uint256)")), "Check": crypto.Keccak256Hash([]byte("Check(address,uint256)")),
}, },
}, },
} }
@ -111,6 +111,39 @@ func TestEventId(t *testing.T) {
} }
} }
func TestEventString(t *testing.T) {
var table = []struct {
definition string
expectations map[string]string
}{
{
definition: `[
{ "type" : "event", "name" : "Balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
{ "type" : "event", "name" : "Check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] },
{ "type" : "event", "name" : "Transfer", "inputs": [{ "name": "from", "type": "address", "indexed": true }, { "name": "to", "type": "address", "indexed": true }, { "name": "value", "type": "uint256" }] }
]`,
expectations: map[string]string{
"Balance": "event Balance(uint256 in)",
"Check": "event Check(address t, uint256 b)",
"Transfer": "event Transfer(address indexed from, address indexed to, uint256 value)",
},
},
}
for _, test := range table {
abi, err := JSON(strings.NewReader(test.definition))
if err != nil {
t.Fatal(err)
}
for name, event := range abi.Events {
if event.String() != test.expectations[name] {
t.Errorf("expected string to be %s, got %s", test.expectations[name], event.String())
}
}
}
}
// TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array. // TestEventMultiValueWithArrayUnpack verifies that array fields will be counted after parsing array.
func TestEventMultiValueWithArrayUnpack(t *testing.T) { func TestEventMultiValueWithArrayUnpack(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]` definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`

View file

@ -56,14 +56,14 @@ func (method Method) Sig() string {
func (method Method) String() string { func (method Method) String() string {
inputs := make([]string, len(method.Inputs)) inputs := make([]string, len(method.Inputs))
for i, input := range method.Inputs { for i, input := range method.Inputs {
inputs[i] = fmt.Sprintf("%v %v", input.Name, input.Type) inputs[i] = fmt.Sprintf("%v %v", input.Type, input.Name)
} }
outputs := make([]string, len(method.Outputs)) outputs := make([]string, len(method.Outputs))
for i, output := range method.Outputs { for i, output := range method.Outputs {
outputs[i] = output.Type.String()
if len(output.Name) > 0 { if len(output.Name) > 0 {
outputs[i] = fmt.Sprintf("%v ", output.Name) outputs[i] += fmt.Sprintf(" %v", output.Name)
} }
outputs[i] += output.Type.String()
} }
constant := "" constant := ""
if method.Const { if method.Const {

View file

@ -0,0 +1,61 @@
// Copyright 2016 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 abi
import (
"strings"
"testing"
)
const methoddata = `
[
{ "type" : "function", "name" : "balance", "constant" : true },
{ "type" : "function", "name" : "send", "constant" : false, "inputs" : [ { "name" : "amount", "type" : "uint256" } ] },
{ "type" : "function", "name" : "transfer", "constant" : false, "inputs" : [ { "name" : "from", "type" : "address" }, { "name" : "to", "type" : "address" }, { "name" : "value", "type" : "uint256" } ], "outputs" : [ { "name" : "success", "type" : "bool" } ] }
]`
func TestMethodString(t *testing.T) {
var table = []struct {
method string
expectation string
}{
{
method: "balance",
expectation: "function balance() constant returns()",
},
{
method: "send",
expectation: "function send(uint256 amount) returns()",
},
{
method: "transfer",
expectation: "function transfer(address from, address to, uint256 value) returns(bool success)",
},
}
abi, err := JSON(strings.NewReader(methoddata))
if err != nil {
t.Fatal(err)
}
for _, test := range table {
got := abi.Methods[test.method].String()
if got != test.expectation {
t.Errorf("expected string to be %s, got %s", test.expectation, got)
}
}
}

View file

@ -30,313 +30,600 @@ import (
func TestPack(t *testing.T) { func TestPack(t *testing.T) {
for i, test := range []struct { for i, test := range []struct {
typ string typ string
components []ArgumentMarshaling
input interface{} input interface{}
output []byte output []byte
}{ }{
{ {
"uint8", "uint8",
nil,
uint8(2), uint8(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint8[]", "uint8[]",
nil,
[]uint8{1, 2}, []uint8{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint16", "uint16",
nil,
uint16(2), uint16(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint16[]", "uint16[]",
nil,
[]uint16{1, 2}, []uint16{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint32", "uint32",
nil,
uint32(2), uint32(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint32[]", "uint32[]",
nil,
[]uint32{1, 2}, []uint32{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint64", "uint64",
nil,
uint64(2), uint64(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint64[]", "uint64[]",
nil,
[]uint64{1, 2}, []uint64{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint256", "uint256",
nil,
big.NewInt(2), big.NewInt(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"uint256[]", "uint256[]",
nil,
[]*big.Int{big.NewInt(1), big.NewInt(2)}, []*big.Int{big.NewInt(1), big.NewInt(2)},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int8", "int8",
nil,
int8(2), int8(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int8[]", "int8[]",
nil,
[]int8{1, 2}, []int8{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int16", "int16",
nil,
int16(2), int16(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int16[]", "int16[]",
nil,
[]int16{1, 2}, []int16{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int32", "int32",
nil,
int32(2), int32(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int32[]", "int32[]",
nil,
[]int32{1, 2}, []int32{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int64", "int64",
nil,
int64(2), int64(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int64[]", "int64[]",
nil,
[]int64{1, 2}, []int64{1, 2},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int256", "int256",
nil,
big.NewInt(2), big.NewInt(2),
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"int256[]", "int256[]",
nil,
[]*big.Int{big.NewInt(1), big.NewInt(2)}, []*big.Int{big.NewInt(1), big.NewInt(2)},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
}, },
{ {
"bytes1", "bytes1",
nil,
[1]byte{1}, [1]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes2", "bytes2",
nil,
[2]byte{1}, [2]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes3", "bytes3",
nil,
[3]byte{1}, [3]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes4", "bytes4",
nil,
[4]byte{1}, [4]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes5", "bytes5",
nil,
[5]byte{1}, [5]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes6", "bytes6",
nil,
[6]byte{1}, [6]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes7", "bytes7",
nil,
[7]byte{1}, [7]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes8", "bytes8",
nil,
[8]byte{1}, [8]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes9", "bytes9",
nil,
[9]byte{1}, [9]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes10", "bytes10",
nil,
[10]byte{1}, [10]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes11", "bytes11",
nil,
[11]byte{1}, [11]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes12", "bytes12",
nil,
[12]byte{1}, [12]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes13", "bytes13",
nil,
[13]byte{1}, [13]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes14", "bytes14",
nil,
[14]byte{1}, [14]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes15", "bytes15",
nil,
[15]byte{1}, [15]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes16", "bytes16",
nil,
[16]byte{1}, [16]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes17", "bytes17",
nil,
[17]byte{1}, [17]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes18", "bytes18",
nil,
[18]byte{1}, [18]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes19", "bytes19",
nil,
[19]byte{1}, [19]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes20", "bytes20",
nil,
[20]byte{1}, [20]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes21", "bytes21",
nil,
[21]byte{1}, [21]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes22", "bytes22",
nil,
[22]byte{1}, [22]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes23", "bytes23",
nil,
[23]byte{1}, [23]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes24", "bytes24",
[24]byte{1}, nil,
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
},
{
"bytes24",
[24]byte{1}, [24]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes25", "bytes25",
nil,
[25]byte{1}, [25]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes26", "bytes26",
nil,
[26]byte{1}, [26]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes27", "bytes27",
nil,
[27]byte{1}, [27]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes28", "bytes28",
nil,
[28]byte{1}, [28]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes29", "bytes29",
nil,
[29]byte{1}, [29]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes30", "bytes30",
nil,
[30]byte{1}, [30]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes31", "bytes31",
nil,
[31]byte{1}, [31]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"bytes32", "bytes32",
nil,
[32]byte{1}, [32]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"uint32[2][3][4]", "uint32[2][3][4]",
nil,
[4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}}, [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
}, },
{ {
"address[]", "address[]",
nil,
[]common.Address{{1}, {2}}, []common.Address{{1}, {2}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"),
}, },
{ {
"bytes32[]", "bytes32[]",
nil,
[]common.Hash{{1}, {2}}, []common.Hash{{1}, {2}},
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"function", "function",
nil,
[24]byte{1}, [24]byte{1},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
}, },
{ {
"string", "string",
nil,
"foobar", "foobar",
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"), common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
}, },
{
"string[]",
nil,
[]string{"hello", "foobar"},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
"0000000000000000000000000000000000000000000000000000000000000080" + // offset 128 to i = 1
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
},
{
"string[2]",
nil,
[]string{"hello", "foobar"},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
},
{
"bytes32[][]",
nil,
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
},
{
"bytes32[][2]",
nil,
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
},
{
"bytes32[3][2]",
nil,
[][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0][2]
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
},
{
// static tuple
"tuple",
[]ArgumentMarshaling{
{Name: "a", Type: "int64"},
{Name: "b", Type: "int256"},
{Name: "c", Type: "int256"},
{Name: "d", Type: "bool"},
{Name: "e", Type: "bytes32[3][2]"},
},
struct {
A int64
B *big.Int
C *big.Int
D bool
E [][]common.Hash
}{1, big.NewInt(1), big.NewInt(-1), true, [][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001" + // struct[a]
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // struct[c]
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[d]
"0100000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][0]
"0200000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][1]
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][2]
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][0]
"0400000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][1]
"0500000000000000000000000000000000000000000000000000000000000000"), // struct[e] array[1][2]
},
{
// dynamic tuple
"tuple",
[]ArgumentMarshaling{
{Name: "a", Type: "string"},
{Name: "b", Type: "int64"},
{Name: "c", Type: "bytes"},
{Name: "d", Type: "string[]"},
{Name: "e", Type: "int256[]"},
{Name: "f", Type: "address[]"},
},
struct {
FieldA string `abi:"a"` // Test whether abi tag works
FieldB int64 `abi:"b"`
C []byte
D []string
E []*big.Int
F []common.Address
}{"foobar", 1, []byte{1}, []string{"foo", "bar"}, []*big.Int{big.NewInt(1), big.NewInt(-1)}, []common.Address{{1}, {2}}},
common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
"0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset
"0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset
"0000000000000000000000000000000000000000000000000000000000000220" + // struct[e] offset
"0000000000000000000000000000000000000000000000000000000000000280" + // struct[f] offset
"0000000000000000000000000000000000000000000000000000000000000006" + // struct[a] length
"666f6f6261720000000000000000000000000000000000000000000000000000" + // struct[a] "foobar"
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[c] length
"0100000000000000000000000000000000000000000000000000000000000000" + // []byte{1}
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[d] length
"0000000000000000000000000000000000000000000000000000000000000040" + // foo offset
"0000000000000000000000000000000000000000000000000000000000000080" + // bar offset
"0000000000000000000000000000000000000000000000000000000000000003" + // foo length
"666f6f0000000000000000000000000000000000000000000000000000000000" + // foo
"0000000000000000000000000000000000000000000000000000000000000003" + // bar offset
"6261720000000000000000000000000000000000000000000000000000000000" + // bar
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[e] length
"0000000000000000000000000000000000000000000000000000000000000001" + // 1
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // -1
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[f] length
"0000000000000000000000000100000000000000000000000000000000000000" + // common.Address{1}
"0000000000000000000000000200000000000000000000000000000000000000"), // common.Address{2}
},
{
// nested tuple
"tuple",
[]ArgumentMarshaling{
{Name: "a", Type: "tuple", Components: []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256[]"}}},
{Name: "b", Type: "int256[]"},
},
struct {
A struct {
FieldA *big.Int `abi:"a"`
B []*big.Int
}
B []*big.Int
}{ }{
typ, err := NewType(test.typ) A: struct {
FieldA *big.Int `abi:"a"` // Test whether abi tag works for nested tuple
B []*big.Int
}{big.NewInt(1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
B: []*big.Int{big.NewInt(1), big.NewInt(0)}},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // a offset
"00000000000000000000000000000000000000000000000000000000000000e0" + // b offset
"0000000000000000000000000000000000000000000000000000000000000001" + // a.a value
"0000000000000000000000000000000000000000000000000000000000000040" + // a.b offset
"0000000000000000000000000000000000000000000000000000000000000002" + // a.b length
"0000000000000000000000000000000000000000000000000000000000000001" + // a.b[0] value
"0000000000000000000000000000000000000000000000000000000000000000" + // a.b[1] value
"0000000000000000000000000000000000000000000000000000000000000002" + // b length
"0000000000000000000000000000000000000000000000000000000000000001" + // b[0] value
"0000000000000000000000000000000000000000000000000000000000000000"), // b[1] value
},
{
// tuple slice
"tuple[]",
[]ArgumentMarshaling{
{Name: "a", Type: "int256"},
{Name: "b", Type: "int256[]"},
},
[]struct {
A *big.Int
B []*big.Int
}{
{big.NewInt(-1), []*big.Int{big.NewInt(1), big.NewInt(0)}},
{big.NewInt(1), []*big.Int{big.NewInt(2), big.NewInt(-1)}},
},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // tuple length
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
"00000000000000000000000000000000000000000000000000000000000000e0" + // tuple[1] offset
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0].B offset
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].B length
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].B[0] value
"0000000000000000000000000000000000000000000000000000000000000000" + // tuple[0].B[1] value
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A
"0000000000000000000000000000000000000000000000000000000000000040" + // tuple[1].B offset
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B length
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].B[0] value
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].B[1] value
},
{
// static tuple array
"tuple[2]",
[]ArgumentMarshaling{
{Name: "a", Type: "int256"},
{Name: "b", Type: "int256"},
},
[2]struct {
A *big.Int
B *big.Int
}{
{big.NewInt(-1), big.NewInt(1)},
{big.NewInt(1), big.NewInt(-1)},
},
common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].a
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].b
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].a
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].b
},
{
// dynamic tuple array
"tuple[2]",
[]ArgumentMarshaling{
{Name: "a", Type: "int256[]"},
},
[2]struct {
A []*big.Int
}{
{[]*big.Int{big.NewInt(-1), big.NewInt(1)}},
{[]*big.Int{big.NewInt(1), big.NewInt(-1)}},
},
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // tuple[0] offset
"00000000000000000000000000000000000000000000000000000000000000c0" + // tuple[1] offset
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[0].A offset
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[0].A length
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // tuple[0].A[0]
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[0].A[1]
"0000000000000000000000000000000000000000000000000000000000000020" + // tuple[1].A offset
"0000000000000000000000000000000000000000000000000000000000000002" + // tuple[1].A length
"0000000000000000000000000000000000000000000000000000000000000001" + // tuple[1].A[0]
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"), // tuple[1].A[1]
},
} {
typ, err := NewType(test.typ, test.components)
if err != nil { if err != nil {
t.Fatalf("%v failed. Unexpected parse error: %v", i, err) t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
} }
output, err := typ.pack(reflect.ValueOf(test.input)) output, err := typ.pack(reflect.ValueOf(test.input))
if err != nil { if err != nil {
t.Fatalf("%v failed. Unexpected pack error: %v", i, err) t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
} }
if !bytes.Equal(output, test.output) { if !bytes.Equal(output, test.output) {
t.Errorf("%d failed. Expected bytes: '%x' Got: '%x'", i, test.output, output) t.Errorf("input %d for typ: %v failed. Expected bytes: '%x' Got: '%x'", i, typ.String(), test.output, output)
} }
} }
} }
@ -406,6 +693,59 @@ func TestMethodPack(t *testing.T) {
if !bytes.Equal(packed, sig) { if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed) t.Errorf("expected %x got %x", sig, packed)
} }
a := [2][2]*big.Int{{big.NewInt(1), big.NewInt(1)}, {big.NewInt(2), big.NewInt(0)}}
sig = abi.Methods["nestedArray"].Id()
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0xa0}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes(addrC[:], 32)...)
sig = append(sig, common.LeftPadBytes(addrD[:], 32)...)
packed, err = abi.Pack("nestedArray", a, []common.Address{addrC, addrD})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
sig = abi.Methods["nestedArray2"].Id()
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x80}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
packed, err = abi.Pack("nestedArray2", [2][]uint8{{1}, {1}})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
sig = abi.Methods["nestedSlice"].Id()
sig = append(sig, common.LeftPadBytes([]byte{0x20}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x02}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0x40}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{0xa0}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{1}, 32)...)
sig = append(sig, common.LeftPadBytes([]byte{2}, 32)...)
packed, err = abi.Pack("nestedSlice", [][]uint8{{1, 2}, {1, 2}})
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(packed, sig) {
t.Errorf("expected %x got %x", sig, packed)
}
} }
func TestPackNumber(t *testing.T) { func TestPackNumber(t *testing.T) {

View file

@ -71,22 +71,36 @@ func mustArrayToByteSlice(value reflect.Value) reflect.Value {
// //
// set is a bit more lenient when it comes to assignment and doesn't force an as // set is a bit more lenient when it comes to assignment and doesn't force an as
// strict ruleset as bare `reflect` does. // strict ruleset as bare `reflect` does.
func set(dst, src reflect.Value, output Argument) error { func set(dst, src reflect.Value) error {
dstType := dst.Type() dstType, srcType := dst.Type(), src.Type()
srcType := src.Type()
switch { switch {
case dstType.AssignableTo(srcType): case dstType.Kind() == reflect.Interface && dst.Elem().IsValid():
return set(dst.Elem(), src)
case dstType.Kind() == reflect.Ptr && dstType.Elem() != derefbigT:
return set(dst.Elem(), src)
case srcType.AssignableTo(dstType) && dst.CanSet():
dst.Set(src) dst.Set(src)
case dstType.Kind() == reflect.Interface: case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice:
dst.Set(src) return setSlice(dst, src)
case dstType.Kind() == reflect.Ptr:
return set(dst.Elem(), src, output)
default: default:
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type()) return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
} }
return nil return nil
} }
// setSlice attempts to assign src to dst when slices are not assignable by default
// e.g. src: [][]byte -> dst: [][15]byte
func setSlice(dst, src reflect.Value) error {
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
for i := 0; i < src.Len(); i++ {
v := src.Index(i)
reflect.Copy(slice.Index(i), v)
}
dst.Set(slice)
return nil
}
// requireAssignable assures that `dest` is a pointer and it's not an interface. // requireAssignable assures that `dest` is a pointer and it's not an interface.
func requireAssignable(dst, src reflect.Value) error { func requireAssignable(dst, src reflect.Value) error {
if dst.Kind() != reflect.Ptr && dst.Kind() != reflect.Interface { if dst.Kind() != reflect.Ptr && dst.Kind() != reflect.Interface {
@ -112,14 +126,14 @@ func requireUnpackKind(v reflect.Value, t reflect.Type, k reflect.Kind,
return nil return nil
} }
// mapAbiToStringField maps abi to struct fields. // mapArgNamesToStructFields maps a slice of argument names to struct fields.
// first round: for each Exportable field that contains a `abi:""` tag // first round: for each Exportable field that contains a `abi:""` tag
// and this field name exists in the arguments, pair them together. // and this field name exists in the given argument name list, pair them together.
// second round: for each argument field that has not been already linked, // second round: for each argument name that has not been already linked,
// find what variable is expected to be mapped into, if it exists and has not been // find what variable is expected to be mapped into, if it exists and has not been
// used, pair them. // used, pair them.
func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]string, error) { // Note this function assumes the given value is a struct value.
func mapArgNamesToStructFields(argNames []string, value reflect.Value) (map[string]string, error) {
typ := value.Type() typ := value.Type()
abi2struct := make(map[string]string) abi2struct := make(map[string]string)
@ -133,45 +147,39 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) { if structFieldName[:1] != strings.ToUpper(structFieldName[:1]) {
continue continue
} }
// skip fields that have no abi:"" tag. // skip fields that have no abi:"" tag.
var ok bool var ok bool
var tagName string var tagName string
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok { if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
continue continue
} }
// check if tag is empty. // check if tag is empty.
if tagName == "" { if tagName == "" {
return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName) return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
} }
// check which argument field matches with the abi tag. // check which argument field matches with the abi tag.
found := false found := false
for _, abiField := range args.NonIndexed() { for _, arg := range argNames {
if abiField.Name == tagName { if arg == tagName {
if abi2struct[abiField.Name] != "" { if abi2struct[arg] != "" {
return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName) return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
} }
// pair them // pair them
abi2struct[abiField.Name] = structFieldName abi2struct[arg] = structFieldName
struct2abi[structFieldName] = abiField.Name struct2abi[structFieldName] = arg
found = true found = true
} }
} }
// check if this tag has been mapped. // check if this tag has been mapped.
if !found { if !found {
return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName) return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
} }
} }
// second round ~~~ // second round ~~~
for _, arg := range args { for _, argName := range argNames {
abiFieldName := arg.Name structFieldName := ToCamelCase(argName)
structFieldName := capitalise(abiFieldName)
if structFieldName == "" { if structFieldName == "" {
return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct") return nil, fmt.Errorf("abi: purely underscored output cannot unpack to struct")
@ -181,11 +189,11 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
// struct field with the same field name. If so, raise an error: // struct field with the same field name. If so, raise an error:
// abi: [ { "name": "value" } ] // abi: [ { "name": "value" } ]
// struct { Value *big.Int , Value1 *big.Int `abi:"value"`} // struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
if abi2struct[abiFieldName] != "" { if abi2struct[argName] != "" {
if abi2struct[abiFieldName] != structFieldName && if abi2struct[argName] != structFieldName &&
struct2abi[structFieldName] == "" && struct2abi[structFieldName] == "" &&
value.FieldByName(structFieldName).IsValid() { value.FieldByName(structFieldName).IsValid() {
return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", abiFieldName) return nil, fmt.Errorf("abi: multiple variables maps to the same abi field '%s'", argName)
} }
continue continue
} }
@ -197,16 +205,14 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
if value.FieldByName(structFieldName).IsValid() { if value.FieldByName(structFieldName).IsValid() {
// pair them // pair them
abi2struct[abiFieldName] = structFieldName abi2struct[argName] = structFieldName
struct2abi[structFieldName] = abiFieldName struct2abi[structFieldName] = argName
} else { } else {
// not paired, but annotate as used, to detect cases like // not paired, but annotate as used, to detect cases like
// abi : [ { "name": "value" }, { "name": "_value" } ] // abi : [ { "name": "value" }, { "name": "_value" } ]
// struct { Value *big.Int } // struct { Value *big.Int }
struct2abi[structFieldName] = abiFieldName struct2abi[structFieldName] = argName
} }
} }
return abi2struct, nil return abi2struct, nil
} }

View file

@ -0,0 +1,191 @@
// Copyright 2019 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 abi
import (
"reflect"
"testing"
)
type reflectTest struct {
name string
args []string
struc interface{}
want map[string]string
err string
}
var reflectTests = []reflectTest{
{
name: "OneToOneCorrespondance",
args: []string{"fieldA"},
struc: struct {
FieldA int `abi:"fieldA"`
}{},
want: map[string]string{
"fieldA": "FieldA",
},
},
{
name: "MissingFieldsInStruct",
args: []string{"fieldA", "fieldB"},
struc: struct {
FieldA int `abi:"fieldA"`
}{},
want: map[string]string{
"fieldA": "FieldA",
},
},
{
name: "MoreFieldsInStructThanArgs",
args: []string{"fieldA"},
struc: struct {
FieldA int `abi:"fieldA"`
FieldB int
}{},
want: map[string]string{
"fieldA": "FieldA",
},
},
{
name: "MissingFieldInArgs",
args: []string{"fieldA"},
struc: struct {
FieldA int `abi:"fieldA"`
FieldB int `abi:"fieldB"`
}{},
err: "struct: abi tag 'fieldB' defined but not found in abi",
},
{
name: "NoAbiDescriptor",
args: []string{"fieldA"},
struc: struct {
FieldA int
}{},
want: map[string]string{
"fieldA": "FieldA",
},
},
{
name: "NoArgs",
args: []string{},
struc: struct {
FieldA int `abi:"fieldA"`
}{},
err: "struct: abi tag 'fieldA' defined but not found in abi",
},
{
name: "DifferentName",
args: []string{"fieldB"},
struc: struct {
FieldA int `abi:"fieldB"`
}{},
want: map[string]string{
"fieldB": "FieldA",
},
},
{
name: "DifferentName",
args: []string{"fieldB"},
struc: struct {
FieldA int `abi:"fieldB"`
}{},
want: map[string]string{
"fieldB": "FieldA",
},
},
{
name: "MultipleFields",
args: []string{"fieldA", "fieldB"},
struc: struct {
FieldA int `abi:"fieldA"`
FieldB int `abi:"fieldB"`
}{},
want: map[string]string{
"fieldA": "FieldA",
"fieldB": "FieldB",
},
},
{
name: "MultipleFieldsABIMissing",
args: []string{"fieldA", "fieldB"},
struc: struct {
FieldA int `abi:"fieldA"`
FieldB int
}{},
want: map[string]string{
"fieldA": "FieldA",
"fieldB": "FieldB",
},
},
{
name: "NameConflict",
args: []string{"fieldB"},
struc: struct {
FieldA int `abi:"fieldB"`
FieldB int
}{},
err: "abi: multiple variables maps to the same abi field 'fieldB'",
},
{
name: "Underscored",
args: []string{"_"},
struc: struct {
FieldA int
}{},
err: "abi: purely underscored output cannot unpack to struct",
},
{
name: "DoubleMapping",
args: []string{"fieldB", "fieldC", "fieldA"},
struc: struct {
FieldA int `abi:"fieldC"`
FieldB int
}{},
err: "abi: multiple outputs mapping to the same struct field 'FieldA'",
},
{
name: "AlreadyMapped",
args: []string{"fieldB", "fieldB"},
struc: struct {
FieldB int `abi:"fieldB"`
}{},
err: "struct: abi tag in 'FieldB' already mapped",
},
}
func TestReflectNameToStruct(t *testing.T) {
for _, test := range reflectTests {
t.Run(test.name, func(t *testing.T) {
m, err := mapArgNamesToStructFields(test.args, reflect.ValueOf(test.struc))
if len(test.err) > 0 {
if err == nil || err.Error() != test.err {
t.Fatalf("Invalid error: expected %v, got %v", test.err, err)
}
} else {
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
for fname := range test.want {
if m[fname] != test.want[fname] {
t.Fatalf("Incorrect value for field %s: expected %v, got %v", fname, test.want[fname], m[fname])
}
}
}
})
}
}

View file

@ -17,6 +17,7 @@
package abi package abi
import ( import (
"errors"
"fmt" "fmt"
"reflect" "reflect"
"regexp" "regexp"
@ -32,6 +33,7 @@ const (
StringTy StringTy
SliceTy SliceTy
ArrayTy ArrayTy
TupleTy
AddressTy AddressTy
FixedBytesTy FixedBytesTy
BytesTy BytesTy
@ -43,13 +45,16 @@ const (
// Type is the reflection of the supported argument type // Type is the reflection of the supported argument type
type Type struct { type Type struct {
Elem *Type Elem *Type
Kind reflect.Kind Kind reflect.Kind
Type reflect.Type Type reflect.Type
Size int Size int
T byte // Our own type checking T byte // Our own type checking
stringKind string // holds the unparsed string for deriving signatures stringKind string // holds the unparsed string for deriving signatures
// Tuple relative fields
TupleElems []*Type // Type information of all tuple fields
TupleRawNames []string // Raw field name of all tuple fields
} }
var ( var (
@ -58,7 +63,7 @@ var (
) )
// NewType creates a new reflection type of abi type given in t. // NewType creates a new reflection type of abi type given in t.
func NewType(t string) (typ Type, err error) { func NewType(t string, components []ArgumentMarshaling) (typ Type, err error) {
// check that array brackets are equal if they exist // check that array brackets are equal if they exist
if strings.Count(t, "[") != strings.Count(t, "]") { if strings.Count(t, "[") != strings.Count(t, "]") {
return Type{}, fmt.Errorf("invalid arg type in abi") return Type{}, fmt.Errorf("invalid arg type in abi")
@ -71,7 +76,7 @@ func NewType(t string) (typ Type, err error) {
if strings.Count(t, "[") != 0 { if strings.Count(t, "[") != 0 {
i := strings.LastIndex(t, "[") i := strings.LastIndex(t, "[")
// recursively embed the type // recursively embed the type
embeddedType, err := NewType(t[:i]) embeddedType, err := NewType(t[:i], components)
if err != nil { if err != nil {
return Type{}, err return Type{}, err
} }
@ -87,6 +92,9 @@ func NewType(t string) (typ Type, err error) {
typ.Kind = reflect.Slice typ.Kind = reflect.Slice
typ.Elem = &embeddedType typ.Elem = &embeddedType
typ.Type = reflect.SliceOf(embeddedType.Type) typ.Type = reflect.SliceOf(embeddedType.Type)
if embeddedType.T == TupleTy {
typ.stringKind = embeddedType.stringKind + sliced
}
} else if len(intz) == 1 { } else if len(intz) == 1 {
// is a array // is a array
typ.T = ArrayTy typ.T = ArrayTy
@ -97,6 +105,9 @@ func NewType(t string) (typ Type, err error) {
return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err) return Type{}, fmt.Errorf("abi: error parsing variable size: %v", err)
} }
typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type) typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
if embeddedType.T == TupleTy {
typ.stringKind = embeddedType.stringKind + sliced
}
} else { } else {
return Type{}, fmt.Errorf("invalid formatting of array type") return Type{}, fmt.Errorf("invalid formatting of array type")
} }
@ -158,6 +169,40 @@ func NewType(t string) (typ Type, err error) {
typ.Size = varSize typ.Size = varSize
typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0))) typ.Type = reflect.ArrayOf(varSize, reflect.TypeOf(byte(0)))
} }
case "tuple":
var (
fields []reflect.StructField
elems []*Type
names []string
expression string // canonical parameter expression
)
expression += "("
for idx, c := range components {
cType, err := NewType(c.Type, c.Components)
if err != nil {
return Type{}, err
}
if ToCamelCase(c.Name) == "" {
return Type{}, errors.New("abi: purely anonymous or underscored field is not supported")
}
fields = append(fields, reflect.StructField{
Name: ToCamelCase(c.Name), // reflect.StructOf will panic for any exported field.
Type: cType.Type,
})
elems = append(elems, &cType)
names = append(names, c.Name)
expression += cType.stringKind
if idx != len(components)-1 {
expression += ","
}
}
expression += ")"
typ.Kind = reflect.Struct
typ.Type = reflect.StructOf(fields)
typ.TupleElems = elems
typ.TupleRawNames = names
typ.T = TupleTy
typ.stringKind = expression
case "function": case "function":
typ.Kind = reflect.Array typ.Kind = reflect.Array
typ.T = FunctionTy typ.T = FunctionTy
@ -178,32 +223,130 @@ func (t Type) String() (out string) {
func (t Type) pack(v reflect.Value) ([]byte, error) { func (t Type) pack(v reflect.Value) ([]byte, error) {
// dereference pointer first if it's a pointer // dereference pointer first if it's a pointer
v = indirect(v) v = indirect(v)
if err := typeCheck(t, v); err != nil { if err := typeCheck(t, v); err != nil {
return nil, err return nil, err
} }
if t.T == SliceTy || t.T == ArrayTy { switch t.T {
var packed []byte case SliceTy, ArrayTy:
var ret []byte
if t.requiresLengthPrefix() {
// append length
ret = append(ret, packNum(reflect.ValueOf(v.Len()))...)
}
// calculate offset if any
offset := 0
offsetReq := isDynamicType(*t.Elem)
if offsetReq {
offset = getTypeSize(*t.Elem) * v.Len()
}
var tail []byte
for i := 0; i < v.Len(); i++ { for i := 0; i < v.Len(); i++ {
val, err := t.Elem.pack(v.Index(i)) val, err := t.Elem.pack(v.Index(i))
if err != nil { if err != nil {
return nil, err return nil, err
} }
packed = append(packed, val...) if !offsetReq {
ret = append(ret, val...)
continue
} }
if t.T == SliceTy { ret = append(ret, packNum(reflect.ValueOf(offset))...)
return packBytesSlice(packed, v.Len()), nil offset += len(val)
} else if t.T == ArrayTy { tail = append(tail, val...)
return packed, nil }
return append(ret, tail...), nil
case TupleTy:
// (T1,...,Tk) for k >= 0 and any types T1, …, Tk
// enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))
// where X = (X(1), ..., X(k)) and head and tail are defined for Ti being a static
// type as
// head(X(i)) = enc(X(i)) and tail(X(i)) = "" (the empty string)
// and as
// head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1))))
// tail(X(i)) = enc(X(i))
// otherwise, i.e. if Ti is a dynamic type.
fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, v)
if err != nil {
return nil, err
}
// Calculate prefix occupied size.
offset := 0
for _, elem := range t.TupleElems {
offset += getTypeSize(*elem)
}
var ret, tail []byte
for i, elem := range t.TupleElems {
field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
if !field.IsValid() {
return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
}
val, err := elem.pack(field)
if err != nil {
return nil, err
}
if isDynamicType(*elem) {
ret = append(ret, packNum(reflect.ValueOf(offset))...)
tail = append(tail, val...)
offset += len(val)
} else {
ret = append(ret, val...)
} }
} }
return append(ret, tail...), nil
default:
return packElement(t, v), nil return packElement(t, v), nil
} }
}
// requireLengthPrefix returns whether the type requires any sort of length // requireLengthPrefix returns whether the type requires any sort of length
// prefixing. // prefixing.
func (t Type) requiresLengthPrefix() bool { func (t Type) requiresLengthPrefix() bool {
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
} }
// isDynamicType returns true if the type is dynamic.
// The following types are called “dynamic”:
// * bytes
// * string
// * T[] for any T
// * T[k] for any dynamic T and any k >= 0
// * (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k
func isDynamicType(t Type) bool {
if t.T == TupleTy {
for _, elem := range t.TupleElems {
if isDynamicType(*elem) {
return true
}
}
return false
}
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
}
// getTypeSize returns the size that this type needs to occupy.
// We distinguish static and dynamic types. Static types are encoded in-place
// and dynamic types are encoded at a separately allocated location after the
// current block.
// So for a static variable, the size returned represents the size that the
// variable actually occupies.
// For a dynamic variable, the returned size is fixed 32 bytes, which is used
// to store the location reference for actual value storage.
func getTypeSize(t Type) int {
if t.T == ArrayTy && !isDynamicType(*t.Elem) {
// Recursively calculate type size if it is a nested array
if t.Elem.T == ArrayTy {
return t.Size * getTypeSize(*t.Elem)
}
return t.Size * 32
} else if t.T == TupleTy && !isDynamicType(t) {
total := 0
for _, elem := range t.TupleElems {
total += getTypeSize(*elem)
}
return total
}
return 32
}

View file

@ -33,71 +33,74 @@ type typeWithoutStringer Type
func TestTypeRegexp(t *testing.T) { func TestTypeRegexp(t *testing.T) {
tests := []struct { tests := []struct {
blob string blob string
components []ArgumentMarshaling
kind Type kind Type
}{ }{
{"bool", Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}}, {"bool", nil, 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[]", nil, 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]", nil, 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[2][]", nil, 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[][]", nil, 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]", nil, 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]", nil, 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]", nil, 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[2][2][2]", nil, 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[][][]", nil, 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][]"}}, {"bool[][2][]", nil, 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: int8T, Size: 8, T: IntTy, stringKind: "int8"}}, {"int8", nil, Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
{"int16", Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}}, {"int16", nil, Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}},
{"int32", Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}}, {"int32", nil, Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}},
{"int64", Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}}, {"int64", nil, Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}},
{"int256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}}, {"int256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}},
{"int8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[]"}}, {"int8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, 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: int8T, Size: 8, T: IntTy, stringKind: "int8"}, stringKind: "int8[2]"}}, {"int8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int8{}), Elem: &Type{Kind: reflect.Int8, Type: int8T, 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: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[]"}}, {"int16[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, 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: int16T, Size: 16, T: IntTy, stringKind: "int16"}, stringKind: "int16[2]"}}, {"int16[2]", nil, Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]int16{}), Elem: &Type{Kind: reflect.Int16, Type: int16T, 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: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[]"}}, {"int32[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, 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: int32T, Size: 32, T: IntTy, stringKind: "int32"}, stringKind: "int32[2]"}}, {"int32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int32{}), Elem: &Type{Kind: reflect.Int32, Type: int32T, 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: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[]"}}, {"int64[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, 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: int64T, Size: 64, T: IntTy, stringKind: "int64"}, stringKind: "int64[2]"}}, {"int64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]int64{}), Elem: &Type{Kind: reflect.Int64, Type: int64T, 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: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[]"}}, {"int256[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, 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: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}}, {"int256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}, stringKind: "int256[2]"}},
{"uint8", Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}}, {"uint8", nil, Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}},
{"uint16", Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}}, {"uint16", nil, Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}},
{"uint32", Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}}, {"uint32", nil, Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}},
{"uint64", Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}}, {"uint64", nil, Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}},
{"uint256", Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}}, {"uint256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}},
{"uint8[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[]"}}, {"uint8[]", nil, Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, 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: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}, stringKind: "uint8[2]"}}, {"uint8[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint8{}), Elem: &Type{Kind: reflect.Uint8, Type: uint8T, 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: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[]"}}, {"uint16[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, 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: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}, stringKind: "uint16[2]"}}, {"uint16[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint16{}), Elem: &Type{Kind: reflect.Uint16, Type: uint16T, 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: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[]"}}, {"uint32[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, 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: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}, stringKind: "uint32[2]"}}, {"uint32[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint32{}), Elem: &Type{Kind: reflect.Uint32, Type: uint32T, 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: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[]"}}, {"uint64[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, 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: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}, stringKind: "uint64[2]"}}, {"uint64[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]uint64{}), Elem: &Type{Kind: reflect.Uint64, Type: uint64T, 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: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[]"}}, {"uint256[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]*big.Int{}), Elem: &Type{Kind: reflect.Ptr, Type: bigT, 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: bigT, Size: 256, T: UintTy, stringKind: "uint256"}, stringKind: "uint256[2]"}}, {"uint256[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]*big.Int{}), Size: 2, Elem: &Type{Kind: reflect.Ptr, Type: bigT, 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"}}, {"bytes32", nil, 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[]", nil, 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]"}}, {"bytes[2]", nil, 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[]", nil, 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]"}}, {"bytes32[2]", nil, 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", nil, 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[]", nil, 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]"}}, {"string[2]", nil, 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: addressT, Size: 20, T: AddressTy, stringKind: "address"}}, {"address", nil, Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}},
{"address[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[]"}}, {"address[]", nil, Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, 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: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}}, {"address[2]", nil, Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]common.Address{}), Elem: &Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, stringKind: "address"}, stringKind: "address[2]"}},
// TODO when fixed types are implemented properly // TODO when fixed types are implemented properly
// {"fixed", Type{}}, // {"fixed", nil, Type{}},
// {"fixed128x128", Type{}}, // {"fixed128x128", nil, Type{}},
// {"fixed[]", Type{}}, // {"fixed[]", nil, Type{}},
// {"fixed[2]", Type{}}, // {"fixed[2]", nil, Type{}},
// {"fixed128x128[]", Type{}}, // {"fixed128x128[]", nil, Type{}},
// {"fixed128x128[2]", Type{}}, // {"fixed128x128[2]", nil, Type{}},
{"tuple", []ArgumentMarshaling{{Name: "a", Type: "int64"}}, Type{Kind: reflect.Struct, T: TupleTy, Type: reflect.TypeOf(struct{ A int64 }{}), stringKind: "(int64)",
TupleElems: []*Type{{Kind: reflect.Int64, T: IntTy, Type: reflect.TypeOf(int64(0)), Size: 64, stringKind: "int64"}}, TupleRawNames: []string{"a"}}},
} }
for _, tt := range tests { for _, tt := range tests {
typ, err := NewType(tt.blob) typ, err := NewType(tt.blob, tt.components)
if err != nil { if err != nil {
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err) t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
} }
@ -110,153 +113,169 @@ func TestTypeRegexp(t *testing.T) {
func TestTypeCheck(t *testing.T) { func TestTypeCheck(t *testing.T) {
for i, test := range []struct { for i, test := range []struct {
typ string typ string
components []ArgumentMarshaling
input interface{} input interface{}
err string err string
}{ }{
{"uint", big.NewInt(1), "unsupported arg type: uint"}, {"uint", nil, big.NewInt(1), "unsupported arg type: uint"},
{"int", big.NewInt(1), "unsupported arg type: int"}, {"int", nil, big.NewInt(1), "unsupported arg type: int"},
{"uint256", big.NewInt(1), ""}, {"uint256", nil, big.NewInt(1), ""},
{"uint256[][3][]", [][3][]*big.Int{{{}}}, ""}, {"uint256[][3][]", nil, [][3][]*big.Int{{{}}}, ""},
{"uint256[][][3]", [3][][]*big.Int{{{}}}, ""}, {"uint256[][][3]", nil, [3][][]*big.Int{{{}}}, ""},
{"uint256[3][][]", [][][3]*big.Int{{{}}}, ""}, {"uint256[3][][]", nil, [][][3]*big.Int{{{}}}, ""},
{"uint256[3][3][3]", [3][3][3]*big.Int{{{}}}, ""}, {"uint256[3][3][3]", nil, [3][3][3]*big.Int{{{}}}, ""},
{"uint8[][]", [][]uint8{}, ""}, {"uint8[][]", nil, [][]uint8{}, ""},
{"int256", big.NewInt(1), ""}, {"int256", nil, big.NewInt(1), ""},
{"uint8", uint8(1), ""}, {"uint8", nil, uint8(1), ""},
{"uint16", uint16(1), ""}, {"uint16", nil, uint16(1), ""},
{"uint32", uint32(1), ""}, {"uint32", nil, uint32(1), ""},
{"uint64", uint64(1), ""}, {"uint64", nil, uint64(1), ""},
{"int8", int8(1), ""}, {"int8", nil, int8(1), ""},
{"int16", int16(1), ""}, {"int16", nil, int16(1), ""},
{"int32", int32(1), ""}, {"int32", nil, int32(1), ""},
{"int64", int64(1), ""}, {"int64", nil, int64(1), ""},
{"uint24", big.NewInt(1), ""}, {"uint24", nil, big.NewInt(1), ""},
{"uint40", big.NewInt(1), ""}, {"uint40", nil, big.NewInt(1), ""},
{"uint48", big.NewInt(1), ""}, {"uint48", nil, big.NewInt(1), ""},
{"uint56", big.NewInt(1), ""}, {"uint56", nil, big.NewInt(1), ""},
{"uint72", big.NewInt(1), ""}, {"uint72", nil, big.NewInt(1), ""},
{"uint80", big.NewInt(1), ""}, {"uint80", nil, big.NewInt(1), ""},
{"uint88", big.NewInt(1), ""}, {"uint88", nil, big.NewInt(1), ""},
{"uint96", big.NewInt(1), ""}, {"uint96", nil, big.NewInt(1), ""},
{"uint104", big.NewInt(1), ""}, {"uint104", nil, big.NewInt(1), ""},
{"uint112", big.NewInt(1), ""}, {"uint112", nil, big.NewInt(1), ""},
{"uint120", big.NewInt(1), ""}, {"uint120", nil, big.NewInt(1), ""},
{"uint128", big.NewInt(1), ""}, {"uint128", nil, big.NewInt(1), ""},
{"uint136", big.NewInt(1), ""}, {"uint136", nil, big.NewInt(1), ""},
{"uint144", big.NewInt(1), ""}, {"uint144", nil, big.NewInt(1), ""},
{"uint152", big.NewInt(1), ""}, {"uint152", nil, big.NewInt(1), ""},
{"uint160", big.NewInt(1), ""}, {"uint160", nil, big.NewInt(1), ""},
{"uint168", big.NewInt(1), ""}, {"uint168", nil, big.NewInt(1), ""},
{"uint176", big.NewInt(1), ""}, {"uint176", nil, big.NewInt(1), ""},
{"uint184", big.NewInt(1), ""}, {"uint184", nil, big.NewInt(1), ""},
{"uint192", big.NewInt(1), ""}, {"uint192", nil, big.NewInt(1), ""},
{"uint200", big.NewInt(1), ""}, {"uint200", nil, big.NewInt(1), ""},
{"uint208", big.NewInt(1), ""}, {"uint208", nil, big.NewInt(1), ""},
{"uint216", big.NewInt(1), ""}, {"uint216", nil, big.NewInt(1), ""},
{"uint224", big.NewInt(1), ""}, {"uint224", nil, big.NewInt(1), ""},
{"uint232", big.NewInt(1), ""}, {"uint232", nil, big.NewInt(1), ""},
{"uint240", big.NewInt(1), ""}, {"uint240", nil, big.NewInt(1), ""},
{"uint248", big.NewInt(1), ""}, {"uint248", nil, big.NewInt(1), ""},
{"int24", big.NewInt(1), ""}, {"int24", nil, big.NewInt(1), ""},
{"int40", big.NewInt(1), ""}, {"int40", nil, big.NewInt(1), ""},
{"int48", big.NewInt(1), ""}, {"int48", nil, big.NewInt(1), ""},
{"int56", big.NewInt(1), ""}, {"int56", nil, big.NewInt(1), ""},
{"int72", big.NewInt(1), ""}, {"int72", nil, big.NewInt(1), ""},
{"int80", big.NewInt(1), ""}, {"int80", nil, big.NewInt(1), ""},
{"int88", big.NewInt(1), ""}, {"int88", nil, big.NewInt(1), ""},
{"int96", big.NewInt(1), ""}, {"int96", nil, big.NewInt(1), ""},
{"int104", big.NewInt(1), ""}, {"int104", nil, big.NewInt(1), ""},
{"int112", big.NewInt(1), ""}, {"int112", nil, big.NewInt(1), ""},
{"int120", big.NewInt(1), ""}, {"int120", nil, big.NewInt(1), ""},
{"int128", big.NewInt(1), ""}, {"int128", nil, big.NewInt(1), ""},
{"int136", big.NewInt(1), ""}, {"int136", nil, big.NewInt(1), ""},
{"int144", big.NewInt(1), ""}, {"int144", nil, big.NewInt(1), ""},
{"int152", big.NewInt(1), ""}, {"int152", nil, big.NewInt(1), ""},
{"int160", big.NewInt(1), ""}, {"int160", nil, big.NewInt(1), ""},
{"int168", big.NewInt(1), ""}, {"int168", nil, big.NewInt(1), ""},
{"int176", big.NewInt(1), ""}, {"int176", nil, big.NewInt(1), ""},
{"int184", big.NewInt(1), ""}, {"int184", nil, big.NewInt(1), ""},
{"int192", big.NewInt(1), ""}, {"int192", nil, big.NewInt(1), ""},
{"int200", big.NewInt(1), ""}, {"int200", nil, big.NewInt(1), ""},
{"int208", big.NewInt(1), ""}, {"int208", nil, big.NewInt(1), ""},
{"int216", big.NewInt(1), ""}, {"int216", nil, big.NewInt(1), ""},
{"int224", big.NewInt(1), ""}, {"int224", nil, big.NewInt(1), ""},
{"int232", big.NewInt(1), ""}, {"int232", nil, big.NewInt(1), ""},
{"int240", big.NewInt(1), ""}, {"int240", nil, big.NewInt(1), ""},
{"int248", big.NewInt(1), ""}, {"int248", nil, big.NewInt(1), ""},
{"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"}, {"uint30", nil, uint8(1), "abi: cannot use uint8 as type ptr as argument"},
{"uint8", uint16(1), "abi: cannot use uint16 as type uint8 as argument"}, {"uint8", nil, uint16(1), "abi: cannot use uint16 as type uint8 as argument"},
{"uint8", uint32(1), "abi: cannot use uint32 as type uint8 as argument"}, {"uint8", nil, uint32(1), "abi: cannot use uint32 as type uint8 as argument"},
{"uint8", uint64(1), "abi: cannot use uint64 as type uint8 as argument"}, {"uint8", nil, uint64(1), "abi: cannot use uint64 as type uint8 as argument"},
{"uint8", int8(1), "abi: cannot use int8 as type uint8 as argument"}, {"uint8", nil, int8(1), "abi: cannot use int8 as type uint8 as argument"},
{"uint8", int16(1), "abi: cannot use int16 as type uint8 as argument"}, {"uint8", nil, int16(1), "abi: cannot use int16 as type uint8 as argument"},
{"uint8", int32(1), "abi: cannot use int32 as type uint8 as argument"}, {"uint8", nil, int32(1), "abi: cannot use int32 as type uint8 as argument"},
{"uint8", int64(1), "abi: cannot use int64 as type uint8 as argument"}, {"uint8", nil, int64(1), "abi: cannot use int64 as type uint8 as argument"},
{"uint16", uint16(1), ""}, {"uint16", nil, uint16(1), ""},
{"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"}, {"uint16", nil, uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
{"uint16[]", []uint16{1, 2, 3}, ""}, {"uint16[]", nil, []uint16{1, 2, 3}, ""},
{"uint16[]", [3]uint16{1, 2, 3}, ""}, {"uint16[]", nil, [3]uint16{1, 2, 3}, ""},
{"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type [0]uint16 as argument"}, {"uint16[]", nil, []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]", nil, [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]", nil, [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"uint16[3]", []uint16{1, 2, 3}, ""}, {"uint16[3]", nil, []uint16{1, 2, 3}, ""},
{"uint16[3]", []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"}, {"uint16[3]", nil, []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
{"address[]", []common.Address{{1}}, ""}, {"address[]", nil, []common.Address{{1}}, ""},
{"address[1]", []common.Address{{1}}, ""}, {"address[1]", nil, []common.Address{{1}}, ""},
{"address[1]", [1]common.Address{{1}}, ""}, {"address[1]", nil, [1]common.Address{{1}}, ""},
{"address[2]", [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"}, {"address[2]", nil, [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
{"bytes32", [32]byte{}, ""}, {"bytes32", nil, [32]byte{}, ""},
{"bytes31", [31]byte{}, ""}, {"bytes31", nil, [31]byte{}, ""},
{"bytes30", [30]byte{}, ""}, {"bytes30", nil, [30]byte{}, ""},
{"bytes29", [29]byte{}, ""}, {"bytes29", nil, [29]byte{}, ""},
{"bytes28", [28]byte{}, ""}, {"bytes28", nil, [28]byte{}, ""},
{"bytes27", [27]byte{}, ""}, {"bytes27", nil, [27]byte{}, ""},
{"bytes26", [26]byte{}, ""}, {"bytes26", nil, [26]byte{}, ""},
{"bytes25", [25]byte{}, ""}, {"bytes25", nil, [25]byte{}, ""},
{"bytes24", [24]byte{}, ""}, {"bytes24", nil, [24]byte{}, ""},
{"bytes23", [23]byte{}, ""}, {"bytes23", nil, [23]byte{}, ""},
{"bytes22", [22]byte{}, ""}, {"bytes22", nil, [22]byte{}, ""},
{"bytes21", [21]byte{}, ""}, {"bytes21", nil, [21]byte{}, ""},
{"bytes20", [20]byte{}, ""}, {"bytes20", nil, [20]byte{}, ""},
{"bytes19", [19]byte{}, ""}, {"bytes19", nil, [19]byte{}, ""},
{"bytes18", [18]byte{}, ""}, {"bytes18", nil, [18]byte{}, ""},
{"bytes17", [17]byte{}, ""}, {"bytes17", nil, [17]byte{}, ""},
{"bytes16", [16]byte{}, ""}, {"bytes16", nil, [16]byte{}, ""},
{"bytes15", [15]byte{}, ""}, {"bytes15", nil, [15]byte{}, ""},
{"bytes14", [14]byte{}, ""}, {"bytes14", nil, [14]byte{}, ""},
{"bytes13", [13]byte{}, ""}, {"bytes13", nil, [13]byte{}, ""},
{"bytes12", [12]byte{}, ""}, {"bytes12", nil, [12]byte{}, ""},
{"bytes11", [11]byte{}, ""}, {"bytes11", nil, [11]byte{}, ""},
{"bytes10", [10]byte{}, ""}, {"bytes10", nil, [10]byte{}, ""},
{"bytes9", [9]byte{}, ""}, {"bytes9", nil, [9]byte{}, ""},
{"bytes8", [8]byte{}, ""}, {"bytes8", nil, [8]byte{}, ""},
{"bytes7", [7]byte{}, ""}, {"bytes7", nil, [7]byte{}, ""},
{"bytes6", [6]byte{}, ""}, {"bytes6", nil, [6]byte{}, ""},
{"bytes5", [5]byte{}, ""}, {"bytes5", nil, [5]byte{}, ""},
{"bytes4", [4]byte{}, ""}, {"bytes4", nil, [4]byte{}, ""},
{"bytes3", [3]byte{}, ""}, {"bytes3", nil, [3]byte{}, ""},
{"bytes2", [2]byte{}, ""}, {"bytes2", nil, [2]byte{}, ""},
{"bytes1", [1]byte{}, ""}, {"bytes1", nil, [1]byte{}, ""},
{"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"}, {"bytes32", nil, [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
{"bytes32", common.Hash{1}, ""}, {"bytes32", nil, common.Hash{1}, ""},
{"bytes31", common.Hash{1}, "abi: cannot use common.Hash as type [31]uint8 as argument"}, {"bytes31", nil, 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"}, {"bytes31", nil, [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
{"bytes", []byte{0, 1}, ""}, {"bytes", nil, []byte{0, 1}, ""},
{"bytes", [2]byte{0, 1}, "abi: cannot use array as type slice as argument"}, {"bytes", nil, [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"}, {"bytes", nil, common.Hash{1}, "abi: cannot use array as type slice as argument"},
{"string", "hello world", ""}, {"string", nil, "hello world", ""},
{"string", string(""), ""}, {"string", nil, string(""), ""},
{"string", []byte{}, "abi: cannot use slice as type string as argument"}, {"string", nil, []byte{}, "abi: cannot use slice as type string as argument"},
{"bytes32[]", [][32]byte{{}}, ""}, {"bytes32[]", nil, [][32]byte{{}}, ""},
{"function", [24]byte{}, ""}, {"function", nil, [24]byte{}, ""},
{"bytes20", common.Address{}, ""}, {"bytes20", nil, common.Address{}, ""},
{"address", [20]byte{}, ""}, {"address", nil, [20]byte{}, ""},
{"address", common.Address{}, ""}, {"address", nil, common.Address{}, ""},
{"bytes32[]]", "", "invalid arg type in abi"}, {"bytes32[]]", nil, "", "invalid arg type in abi"},
{"invalidType", "", "unsupported arg type: invalidType"}, {"invalidType", nil, "", "unsupported arg type: invalidType"},
{"invalidSlice[]", "", "unsupported arg type: invalidSlice"}, {"invalidSlice[]", nil, "", "unsupported arg type: invalidSlice"},
// simple tuple
{"tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256"}}, struct {
A *big.Int
B *big.Int
}{}, ""},
// tuple slice
{"tuple[]", []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256"}}, []struct {
A *big.Int
B *big.Int
}{}, ""},
// tuple array
{"tuple[2]", []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256"}}, []struct {
A *big.Int
B *big.Int
}{{big.NewInt(0), big.NewInt(0)}, {big.NewInt(0), big.NewInt(0)}}, ""},
} { } {
typ, err := NewType(test.typ) typ, err := NewType(test.typ, test.components)
if err != nil && len(test.err) == 0 { if err != nil && len(test.err) == 0 {
t.Fatal("unexpected parse error:", err) t.Fatal("unexpected parse error:", err)
} else if err != nil && len(test.err) != 0 { } else if err != nil && len(test.err) != 0 {

View file

@ -115,17 +115,6 @@ func readFixedBytes(t Type, word []byte) (interface{}, error) {
} }
func getFullElemSize(elem *Type) int {
//all other should be counted as 32 (slices have pointers to respective elements)
size := 32
//arrays wrap it, each element being the same size
for elem.T == ArrayTy {
size *= elem.Size
elem = elem.Elem
}
return size
}
// iteratively unpack elements // iteratively unpack elements
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) { func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
if size < 0 { if size < 0 {
@ -150,13 +139,9 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
// Arrays have packed elements, resulting in longer unpack steps. // Arrays have packed elements, resulting in longer unpack steps.
// Slices have just 32 bytes per element (pointing to the contents). // Slices have just 32 bytes per element (pointing to the contents).
elemSize := 32 elemSize := getTypeSize(*t.Elem)
if t.T == ArrayTy {
elemSize = getFullElemSize(t.Elem)
}
for i, j := start, 0; j < size; i, j = i+elemSize, j+1 { for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
inter, err := toGoType(i, *t.Elem, output) inter, err := toGoType(i, *t.Elem, output)
if err != nil { if err != nil {
return nil, err return nil, err
@ -170,6 +155,36 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
return refSlice.Interface(), nil return refSlice.Interface(), nil
} }
func forTupleUnpack(t Type, output []byte) (interface{}, error) {
retval := reflect.New(t.Type).Elem()
virtualArgs := 0
for index, elem := range t.TupleElems {
marshalledValue, err := toGoType((index+virtualArgs)*32, *elem, output)
if elem.T == ArrayTy && !isDynamicType(*elem) {
// If we have a static array, like [3]uint256, these are coded as
// just like uint256,uint256,uint256.
// This means that we need to add two 'virtual' arguments when
// we count the index from now on.
//
// Array values nested multiple levels deep are also encoded inline:
// [2][3]uint256: uint256,uint256,uint256,uint256,uint256,uint256
//
// Calculate the full array size to get the correct offset for the next argument.
// Decrement it by 1, as the normal index increment is still applied.
virtualArgs += getTypeSize(*elem)/32 - 1
} else if elem.T == TupleTy && !isDynamicType(*elem) {
// If we have a static tuple, like (uint256, bool, uint256), these are
// coded as just like uint256,bool,uint256
virtualArgs += getTypeSize(*elem)/32 - 1
}
if err != nil {
return nil, err
}
retval.Field(index).Set(reflect.ValueOf(marshalledValue))
}
return retval.Interface(), nil
}
// toGoType parses the output bytes and recursively assigns the value of these bytes // toGoType parses the output bytes and recursively assigns the value of these bytes
// into a go type with accordance with the ABI spec. // into a go type with accordance with the ABI spec.
func toGoType(index int, t Type, output []byte) (interface{}, error) { func toGoType(index int, t Type, output []byte) (interface{}, error) {
@ -179,13 +194,13 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
var ( var (
returnOutput []byte returnOutput []byte
begin, end int begin, length int
err error err error
) )
// if we require a length prefix, find the beginning word and size returned. // if we require a length prefix, find the beginning word and size returned.
if t.requiresLengthPrefix() { if t.requiresLengthPrefix() {
begin, end, err = lengthPrefixPointsTo(index, output) begin, length, err = lengthPrefixPointsTo(index, output)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -194,12 +209,26 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
} }
switch t.T { switch t.T {
case TupleTy:
if isDynamicType(t) {
begin, err := tuplePointsTo(index, output)
if err != nil {
return nil, err
}
return forTupleUnpack(t, output[begin:])
} else {
return forTupleUnpack(t, output[index:])
}
case SliceTy: case SliceTy:
return forEachUnpack(t, output, begin, end) return forEachUnpack(t, output[begin:], 0, length)
case ArrayTy: case ArrayTy:
return forEachUnpack(t, output, index, t.Size) if isDynamicType(*t.Elem) {
offset := int64(binary.BigEndian.Uint64(returnOutput[len(returnOutput)-8:]))
return forEachUnpack(t, output[offset:], 0, t.Size)
}
return forEachUnpack(t, output[index:], 0, t.Size)
case StringTy: // variable arrays are written at the end of the return bytes case StringTy: // variable arrays are written at the end of the return bytes
return string(output[begin : begin+end]), nil return string(output[begin : begin+length]), nil
case IntTy, UintTy: case IntTy, UintTy:
return readInteger(t.T, t.Kind, returnOutput), nil return readInteger(t.T, t.Kind, returnOutput), nil
case BoolTy: case BoolTy:
@ -209,7 +238,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
case HashTy: case HashTy:
return common.BytesToHash(returnOutput), nil return common.BytesToHash(returnOutput), nil
case BytesTy: case BytesTy:
return output[begin : begin+end], nil return output[begin : begin+length], nil
case FixedBytesTy: case FixedBytesTy:
return readFixedBytes(t, returnOutput) return readFixedBytes(t, returnOutput)
case FunctionTy: case FunctionTy:
@ -250,3 +279,17 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
length = int(lengthBig.Uint64()) length = int(lengthBig.Uint64())
return return
} }
// tuplePointsTo resolves the location reference for dynamic tuple.
func tuplePointsTo(index int, output []byte) (start int, err error) {
offset := big.NewInt(0).SetBytes(output[index : index+32])
outputLen := big.NewInt(int64(len(output)))
if offset.Cmp(big.NewInt(int64(len(output)))) > 0 {
return 0, fmt.Errorf("abi: cannot marshal in to go slice: offset %v would go over slice boundary (len=%v)", offset, outputLen)
}
if offset.BitLen() > 63 {
return 0, fmt.Errorf("abi offset larger than int64: %v", offset)
}
return int(offset.Uint64()), nil
}

View file

@ -173,9 +173,14 @@ var unpackTests = []unpackTest{
// multi dimensional, if these pass, all types that don't require length prefix should pass // multi dimensional, if these pass, all types that don't require length prefix should pass
{ {
def: `[{"type": "uint8[][]"}]`, def: `[{"type": "uint8[][]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: [][]uint8{{1, 2}, {1, 2}}, want: [][]uint8{{1, 2}, {1, 2}},
}, },
{
def: `[{"type": "uint8[][]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
want: [][]uint8{{1, 2}, {1, 2, 3}},
},
{ {
def: `[{"type": "uint8[2][2]"}]`, def: `[{"type": "uint8[2][2]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@ -183,7 +188,7 @@ var unpackTests = []unpackTest{
}, },
{ {
def: `[{"type": "uint8[][2]"}]`, def: `[{"type": "uint8[][2]"}]`,
enc: "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
want: [2][]uint8{{1}, {1}}, want: [2][]uint8{{1}, {1}},
}, },
{ {
@ -191,6 +196,11 @@ var unpackTests = []unpackTest{
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: [][2]uint8{{1, 2}}, want: [][2]uint8{{1, 2}},
}, },
{
def: `[{"type": "uint8[2][]"}]`,
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: [][2]uint8{{1, 2}, {1, 2}},
},
{ {
def: `[{"type": "uint16[]"}]`, def: `[{"type": "uint16[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@ -236,6 +246,26 @@ var unpackTests = []unpackTest{
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)}, want: [3]*big.Int{big.NewInt(1), big.NewInt(2), big.NewInt(3)},
}, },
{
def: `[{"type": "string[4]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000548656c6c6f0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005576f726c64000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b476f2d657468657265756d0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000",
want: [4]string{"Hello", "World", "Go-ethereum", "Ethereum"},
},
{
def: `[{"type": "string[]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b676f2d657468657265756d000000000000000000000000000000000000000000",
want: []string{"Ethereum", "go-ethereum"},
},
{
def: `[{"type": "bytes[]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000003f0f0f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003f0f0f00000000000000000000000000000000000000000000000000000000000",
want: [][]byte{{0xf0, 0xf0, 0xf0}, {0xf0, 0xf0, 0xf0}},
},
{
def: `[{"type": "uint256[2][][]"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000e00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003e80000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000c8000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000003e8",
want: [][][2]*big.Int{{{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}, {{big.NewInt(1), big.NewInt(200)}, {big.NewInt(1), big.NewInt(1000)}}},
},
{ {
def: `[{"type": "int8[]"}]`, def: `[{"type": "int8[]"}]`,
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@ -295,6 +325,53 @@ var unpackTests = []unpackTest{
Int2 *big.Int Int2 *big.Int
}{big.NewInt(1), big.NewInt(2)}, }{big.NewInt(1), big.NewInt(2)},
}, },
{
def: `[{"name":"int_one","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
IntOne *big.Int
}{big.NewInt(1)},
},
{
def: `[{"name":"int__one","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
IntOne *big.Int
}{big.NewInt(1)},
},
{
def: `[{"name":"int_one_","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
IntOne *big.Int
}{big.NewInt(1)},
},
{
def: `[{"name":"int_one","type":"int256"}, {"name":"intone","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
IntOne *big.Int
Intone *big.Int
}{big.NewInt(1), big.NewInt(2)},
},
{
def: `[{"name":"___","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
IntOne *big.Int
Intone *big.Int
}{},
err: "abi: purely underscored output cannot unpack to struct",
},
{
def: `[{"name":"int_one","type":"int256"},{"name":"IntOne","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
want: struct {
Int1 *big.Int
Int2 *big.Int
}{},
err: "abi: multiple outputs mapping to the same struct field 'IntOne'",
},
{ {
def: `[{"name":"int","type":"int256"},{"name":"Int","type":"int256"}]`, def: `[{"name":"int","type":"int256"},{"name":"Int","type":"int256"}]`,
enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002", enc: "00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
@ -359,6 +436,55 @@ func TestUnpack(t *testing.T) {
} }
} }
func TestUnpackSetDynamicArrayOutput(t *testing.T) {
abi, err := JSON(strings.NewReader(`[{"constant":true,"inputs":[],"name":"testDynamicFixedBytes15","outputs":[{"name":"","type":"bytes15[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"testDynamicFixedBytes32","outputs":[{"name":"","type":"bytes32[]"}],"payable":false,"stateMutability":"view","type":"function"}]`))
if err != nil {
t.Fatal(err)
}
var (
marshalledReturn32 = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000230783132333435363738393000000000000000000000000000000000000000003078303938373635343332310000000000000000000000000000000000000000")
marshalledReturn15 = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000230783031323334350000000000000000000000000000000000000000000000003078393837363534000000000000000000000000000000000000000000000000")
out32 [][32]byte
out15 [][15]byte
)
// test 32
err = abi.Unpack(&out32, "testDynamicFixedBytes32", marshalledReturn32)
if err != nil {
t.Fatal(err)
}
if len(out32) != 2 {
t.Fatalf("expected array with 2 values, got %d", len(out32))
}
expected := common.Hex2Bytes("3078313233343536373839300000000000000000000000000000000000000000")
if !bytes.Equal(out32[0][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out32[0])
}
expected = common.Hex2Bytes("3078303938373635343332310000000000000000000000000000000000000000")
if !bytes.Equal(out32[1][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out32[1])
}
// test 15
err = abi.Unpack(&out15, "testDynamicFixedBytes32", marshalledReturn15)
if err != nil {
t.Fatal(err)
}
if len(out15) != 2 {
t.Fatalf("expected array with 2 values, got %d", len(out15))
}
expected = common.Hex2Bytes("307830313233343500000000000000")
if !bytes.Equal(out15[0][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out15[0])
}
expected = common.Hex2Bytes("307839383736353400000000000000")
if !bytes.Equal(out15[1][:], expected) {
t.Errorf("expected %x, got %x\n", expected, out15[1])
}
}
type methodMultiOutput struct { type methodMultiOutput struct {
Int *big.Int Int *big.Int
String string String string
@ -386,6 +512,11 @@ func TestMethodMultiReturn(t *testing.T) {
Int *big.Int Int *big.Int
} }
newInterfaceSlice := func(len int) interface{} {
slice := make([]interface{}, len)
return &slice
}
abi, data, expected := methodMultiReturn(require.New(t)) abi, data, expected := methodMultiReturn(require.New(t))
bigint := new(big.Int) bigint := new(big.Int)
var testCases = []struct { var testCases = []struct {
@ -413,6 +544,16 @@ func TestMethodMultiReturn(t *testing.T) {
&[2]interface{}{&expected.Int, &expected.String}, &[2]interface{}{&expected.Int, &expected.String},
"", "",
"Can unpack into an array", "Can unpack into an array",
}, {
&[2]interface{}{},
&[2]interface{}{expected.Int, expected.String},
"",
"Can unpack into interface array",
}, {
newInterfaceSlice(2),
&[]interface{}{expected.Int, expected.String},
"",
"Can unpack into interface slice",
}, { }, {
&[]interface{}{new(int), new(int)}, &[]interface{}{new(int), new(int)},
&[]interface{}{&expected.Int, &expected.String}, &[]interface{}{&expected.Int, &expected.String},
@ -462,6 +603,68 @@ func TestMultiReturnWithArray(t *testing.T) {
} }
} }
func TestMultiReturnWithStringArray(t *testing.T) {
const definition = `[{"name" : "multi", "outputs": [{"name": "","type": "uint256[3]"},{"name": "","type": "address"},{"name": "","type": "string[2]"},{"name": "","type": "bool"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000005c1b78ea0000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000001a055690d9db80000000000000000000000000000ab1257528b3782fb40d7ed5f72e624b744dffb2f00000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000008457468657265756d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001048656c6c6f2c20457468657265756d2100000000000000000000000000000000"))
temp, _ := big.NewInt(0).SetString("30000000000000000000", 10)
ret1, ret1Exp := new([3]*big.Int), [3]*big.Int{big.NewInt(1545304298), big.NewInt(6), temp}
ret2, ret2Exp := new(common.Address), common.HexToAddress("ab1257528b3782fb40d7ed5f72e624b744dffb2f")
ret3, ret3Exp := new([2]string), [2]string{"Ethereum", "Hello, Ethereum!"}
ret4, ret4Exp := new(bool), false
if err := abi.Unpack(&[]interface{}{ret1, ret2, ret3, ret4}, "multi", buff.Bytes()); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*ret1, ret1Exp) {
t.Error("big.Int array result", *ret1, "!= Expected", ret1Exp)
}
if !reflect.DeepEqual(*ret2, ret2Exp) {
t.Error("address result", *ret2, "!= Expected", ret2Exp)
}
if !reflect.DeepEqual(*ret3, ret3Exp) {
t.Error("string array result", *ret3, "!= Expected", ret3Exp)
}
if !reflect.DeepEqual(*ret4, ret4Exp) {
t.Error("bool result", *ret4, "!= Expected", ret4Exp)
}
}
func TestMultiReturnWithStringSlice(t *testing.T) {
const definition = `[{"name" : "multi", "outputs": [{"name": "","type": "string[]"},{"name": "","type": "uint256[]"}]}]`
abi, err := JSON(strings.NewReader(definition))
if err != nil {
t.Fatal(err)
}
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0] offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000120")) // output[1] offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[0] length
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040")) // output[0][0] offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // output[0][1] offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000008")) // output[0][0] length
buff.Write(common.Hex2Bytes("657468657265756d000000000000000000000000000000000000000000000000")) // output[0][0] value
buff.Write(common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000b")) // output[0][1] length
buff.Write(common.Hex2Bytes("676f2d657468657265756d000000000000000000000000000000000000000000")) // output[0][1] value
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // output[1] length
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000064")) // output[1][0] value
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000065")) // output[1][1] value
ret1, ret1Exp := new([]string), []string{"ethereum", "go-ethereum"}
ret2, ret2Exp := new([]*big.Int), []*big.Int{big.NewInt(100), big.NewInt(101)}
if err := abi.Unpack(&[]interface{}{ret1, ret2}, "multi", buff.Bytes()); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(*ret1, ret1Exp) {
t.Error("string slice result", *ret1, "!= Expected", ret1Exp)
}
if !reflect.DeepEqual(*ret2, ret2Exp) {
t.Error("uint256 slice result", *ret2, "!= Expected", ret2Exp)
}
}
func TestMultiReturnWithDeeplyNestedArray(t *testing.T) { func TestMultiReturnWithDeeplyNestedArray(t *testing.T) {
// Similar to TestMultiReturnWithArray, but with a special case in mind: // Similar to TestMultiReturnWithArray, but with a special case in mind:
// values of nested static arrays count towards the size as well, and any element following // values of nested static arrays count towards the size as well, and any element following
@ -751,6 +954,108 @@ func TestUnmarshal(t *testing.T) {
} }
} }
func TestUnpackTuple(t *testing.T) {
const simpleTuple = `[{"name":"tuple","constant":false,"outputs":[{"type":"tuple","name":"ret","components":[{"type":"int256","name":"a"},{"type":"int256","name":"b"}]}]}]`
abi, err := JSON(strings.NewReader(simpleTuple))
if err != nil {
t.Fatal(err)
}
buff := new(bytes.Buffer)
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // ret[a] = 1
buff.Write(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) // ret[b] = -1
v := struct {
Ret struct {
A *big.Int
B *big.Int
}
}{Ret: struct {
A *big.Int
B *big.Int
}{new(big.Int), new(big.Int)}}
err = abi.Unpack(&v, "tuple", buff.Bytes())
if err != nil {
t.Error(err)
} else {
if v.Ret.A.Cmp(big.NewInt(1)) != 0 {
t.Errorf("unexpected value unpacked: want %x, got %x", 1, v.Ret.A)
}
if v.Ret.B.Cmp(big.NewInt(-1)) != 0 {
t.Errorf("unexpected value unpacked: want %x, got %x", v.Ret.B, -1)
}
}
// Test nested tuple
const nestedTuple = `[{"name":"tuple","constant":false,"outputs":[
{"type":"tuple","name":"s","components":[{"type":"uint256","name":"a"},{"type":"uint256[]","name":"b"},{"type":"tuple[]","name":"c","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]}]},
{"type":"tuple","name":"t","components":[{"name":"x", "type":"uint256"},{"name":"y","type":"uint256"}]},
{"type":"uint256","name":"a"}
]}]`
abi, err = JSON(strings.NewReader(nestedTuple))
if err != nil {
t.Fatal(err)
}
buff.Reset()
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000080")) // s offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")) // t.X = 0
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // t.Y = 1
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // a = 1
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.A = 1
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000060")) // s.B offset
buff.Write(common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000c0")) // s.C offset
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.B length
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.B[0] = 1
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.B[0] = 2
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.C length
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.C[0].X
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.C[0].Y
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002")) // s.C[1].X
buff.Write(common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001")) // s.C[1].Y
type T struct {
X *big.Int `abi:"x"`
Z *big.Int `abi:"y"` // Test whether the abi tag works.
}
type S struct {
A *big.Int
B []*big.Int
C []T
}
type Ret struct {
FieldS S `abi:"s"`
FieldT T `abi:"t"`
A *big.Int
}
var ret Ret
var expected = Ret{
FieldS: S{
A: big.NewInt(1),
B: []*big.Int{big.NewInt(1), big.NewInt(2)},
C: []T{
{big.NewInt(1), big.NewInt(2)},
{big.NewInt(2), big.NewInt(1)},
},
},
FieldT: T{
big.NewInt(0), big.NewInt(1),
},
A: big.NewInt(1),
}
err = abi.Unpack(&ret, "tuple", buff.Bytes())
if err != nil {
t.Error(err)
}
if reflect.DeepEqual(ret, expected) {
t.Error("unexpected unpack value")
}
}
func TestOOMMaliciousInput(t *testing.T) { func TestOOMMaliciousInput(t *testing.T) {
oomTests := []unpackTest{ oomTests := []unpackTest{
{ {

View file

@ -265,7 +265,10 @@ func (ac *accountCache) scanAccounts() error {
case (addr == common.Address{}): case (addr == common.Address{}):
log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address") log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
default: default:
return &accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}} return &accounts.Account{
Address: addr,
URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
}
} }
return nil return nil
} }

View file

@ -171,7 +171,10 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
if err != nil { if err != nil {
return nil, accounts.Account{}, err return nil, accounts.Account{}, err
} }
a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}} a := accounts.Account{
Address: key.Address,
URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
}
if err := ks.StoreKey(a.URL.Path, key, auth); err != nil { if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
zeroKey(key.PrivateKey) zeroKey(key.PrivateKey)
return nil, a, err return nil, a, err
@ -224,5 +227,6 @@ func toISO8601(t time.Time) string {
} else { } else {
tz = fmt.Sprintf("%03d00", offset/3600) tz = fmt.Sprintf("%03d00", offset/3600)
} }
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz) return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s",
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
} }

View file

@ -233,6 +233,7 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
PrivateKey: key, PrivateKey: key,
}, nil }, nil
} }
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) { func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
if cryptoJson.Cipher != "aes-128-ctr" { if cryptoJson.Cipher != "aes-128-ctr" {
return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher) return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)

View file

@ -38,7 +38,13 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
return accounts.Account{}, nil, err return accounts.Account{}, nil, err
} }
key.Id = uuid.NewRandom() key.Id = uuid.NewRandom()
a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: keyStore.JoinPath(keyFileName(key.Address))}} a := accounts.Account{
Address: key.Address,
URL: accounts.URL{
Scheme: KeyStoreScheme,
Path: keyStore.JoinPath(keyFileName(key.Address)),
},
}
err = keyStore.StoreKey(a.URL.Path, key, password) err = keyStore.StoreKey(a.URL.Path, key, password)
return a, key, err return a, key, err
} }

View file

@ -52,8 +52,8 @@ func (w *keystoreWallet) Status() (string, error) {
// is no connection or decryption step necessary to access the list of accounts. // is no connection or decryption step necessary to access the list of accounts.
func (w *keystoreWallet) Open(passphrase string) error { return nil } func (w *keystoreWallet) Open(passphrase string) error { return nil }
// Close implements accounts.Wallet, but is a noop for plain wallets since is no // Close implements accounts.Wallet, but is a noop for plain wallets since there
// meaningful open operation. // is no meaningful open operation.
func (w *keystoreWallet) Close() error { return nil } func (w *keystoreWallet) Close() error { return nil }
// Accounts implements accounts.Wallet, returning an account list consisting of // Accounts implements accounts.Wallet, returning an account list consisting of
@ -84,10 +84,7 @@ func (w *keystoreWallet) SelfDerive(base accounts.DerivationPath, chain ethereum
// able to sign via our shared keystore backend). // able to sign via our shared keystore backend).
func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) { func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if account.Address != w.account.Address { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
} }
// Account seems valid, request the keystore to sign // Account seems valid, request the keystore to sign
@ -100,10 +97,7 @@ func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte
// be able to sign via our shared keystore backend). // be able to sign via our shared keystore backend).
func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if account.Address != w.account.Address { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
} }
// Account seems valid, request the keystore to sign // Account seems valid, request the keystore to sign
@ -114,10 +108,7 @@ func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction,
// given hash with the given account using passphrase as extra authentication. // given hash with the given account using passphrase as extra authentication.
func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) { func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if account.Address != w.account.Address { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
} }
// Account seems valid, request the keystore to sign // Account seems valid, request the keystore to sign
@ -128,10 +119,7 @@ func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passph
// transaction with the given account using passphrase as extra authentication. // transaction with the given account using passphrase as extra authentication.
func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) { func (w *keystoreWallet) SignTxWithPassphrase(account accounts.Account, passphrase string, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
// Make sure the requested account is contained within // Make sure the requested account is contained within
if account.Address != w.account.Address { if !w.Contains(account) {
return nil, accounts.ErrUnknownAccount
}
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
return nil, accounts.ErrUnknownAccount return nil, accounts.ErrUnknownAccount
} }
// Account seems valid, request the keystore to sign // Account seems valid, request the keystore to sign

View file

@ -257,7 +257,9 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
// Decode the hex sting into an Ethereum address and return // Decode the hex sting into an Ethereum address and return
var address common.Address var address common.Address
hex.Decode(address[:], hexstr) if _, err = hex.Decode(address[:], hexstr); err != nil {
return common.Address{}, err
}
return address, nil return address, nil
} }

View file

@ -41,6 +41,9 @@ import (
// encoded passphrase. // encoded passphrase.
var ErrTrezorPINNeeded = errors.New("trezor: pin needed") var ErrTrezorPINNeeded = errors.New("trezor: pin needed")
// ErrTrezorPassphraseNeeded is returned if opening the trezor requires a passphrase
var ErrTrezorPassphraseNeeded = errors.New("trezor: passphrase needed")
// errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange // errTrezorReplyInvalidHeader is the error message returned by a Trezor data exchange
// if the device replies with a mismatching header. This usually means the device // if the device replies with a mismatching header. This usually means the device
// is in browser mode. // is in browser mode.
@ -52,6 +55,7 @@ type trezorDriver struct {
version [3]uint32 // Current version of the Trezor firmware version [3]uint32 // Current version of the Trezor firmware
label string // Current textual label of the Trezor device label string // Current textual label of the Trezor device
pinwait bool // Flags whether the device is waiting for PIN entry pinwait bool // Flags whether the device is waiting for PIN entry
passphrasewait bool // Flags whether the device is waiting for passphrase entry
failure error // Any failure that would make the device unusable failure error // Any failure that would make the device unusable
log log.Logger // Contextual logger to tag the trezor with its id log log.Logger // Contextual logger to tag the trezor with its id
} }
@ -79,7 +83,7 @@ func (w *trezorDriver) Status() (string, error) {
} }
// Open implements usbwallet.driver, attempting to initialize the connection to // Open implements usbwallet.driver, attempting to initialize the connection to
// the Trezor hardware wallet. Initializing the Trezor is a two phase operation: // the Trezor hardware wallet. Initializing the Trezor is a two or three phase operation:
// * The first phase is to initialize the connection and read the wallet's // * The first phase is to initialize the connection and read the wallet's
// features. This phase is invoked is the provided passphrase is empty. The // features. This phase is invoked is the provided passphrase is empty. The
// device will display the pinpad as a result and will return an appropriate // device will display the pinpad as a result and will return an appropriate
@ -87,11 +91,13 @@ func (w *trezorDriver) Status() (string, error) {
// * The second phase is to unlock access to the Trezor, which is done by the // * The second phase is to unlock access to the Trezor, which is done by the
// user actually providing a passphrase mapping a keyboard keypad to the pin // user actually providing a passphrase mapping a keyboard keypad to the pin
// number of the user (shuffled according to the pinpad displayed). // number of the user (shuffled according to the pinpad displayed).
// * If needed the device will ask for passphrase which will require calling
// open again with the actual passphrase (3rd phase)
func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error { func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
w.device, w.failure = device, nil w.device, w.failure = device, nil
// If phase 1 is requested, init the connection and wait for user callback // If phase 1 is requested, init the connection and wait for user callback
if passphrase == "" { if passphrase == "" && !w.passphrasewait {
// If we're already waiting for a PIN entry, insta-return // If we're already waiting for a PIN entry, insta-return
if w.pinwait { if w.pinwait {
return ErrTrezorPINNeeded return ErrTrezorPINNeeded
@ -104,26 +110,46 @@ func (w *trezorDriver) Open(device io.ReadWriter, passphrase string) error {
w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()} w.version = [3]uint32{features.GetMajorVersion(), features.GetMinorVersion(), features.GetPatchVersion()}
w.label = features.GetLabel() w.label = features.GetLabel()
// Do a manual ping, forcing the device to ask for its PIN // Do a manual ping, forcing the device to ask for its PIN and Passphrase
askPin := true askPin := true
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin}, new(trezor.PinMatrixRequest), new(trezor.Success)) askPassphrase := true
res, err := w.trezorExchange(&trezor.Ping{PinProtection: &askPin, PassphraseProtection: &askPassphrase}, new(trezor.PinMatrixRequest), new(trezor.PassphraseRequest), new(trezor.Success))
if err != nil { if err != nil {
return err return err
} }
// Only return the PIN request if the device wasn't unlocked until now // Only return the PIN request if the device wasn't unlocked until now
if res == 1 { switch res {
return nil // Device responded with trezor.Success case 0:
}
w.pinwait = true w.pinwait = true
return ErrTrezorPINNeeded return ErrTrezorPINNeeded
case 1:
w.pinwait = false
w.passphrasewait = true
return ErrTrezorPassphraseNeeded
case 2:
return nil // responded with trezor.Success
}
} }
// Phase 2 requested with actual PIN entry // Phase 2 requested with actual PIN entry
if w.pinwait {
w.pinwait = false w.pinwait = false
res, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success), new(trezor.PassphraseRequest))
if _, err := w.trezorExchange(&trezor.PinMatrixAck{Pin: &passphrase}, new(trezor.Success)); err != nil { if err != nil {
w.failure = err w.failure = err
return err return err
} }
if res == 1 {
w.passphrasewait = true
return ErrTrezorPassphraseNeeded
}
} else if w.passphrasewait {
w.passphrasewait = false
if _, err := w.trezorExchange(&trezor.PassphraseAck{Passphrase: &passphrase}, new(trezor.Success)); err != nil {
w.failure = err
return err
}
}
return nil return nil
} }

View file

@ -23,8 +23,8 @@ environment:
install: install:
- git submodule update --init - git submodule update --init
- rmdir C:\go /s /q - rmdir C:\go /s /q
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.11.2.windows-%GETH_ARCH%.zip - appveyor DownloadFile https://storage.googleapis.com/golang/go1.11.4.windows-%GETH_ARCH%.zip
- 7z x go1.11.2.windows-%GETH_ARCH%.zip -y -oC:\ > NUL - 7z x go1.11.4.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
- go version - go version
- gcc --version - gcc --version

View file

@ -1,3 +1,19 @@
// Copyright 2018 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/>.
// +build none // +build none
/* /*

View file

@ -110,6 +110,11 @@ var (
Name: "nostack", Name: "nostack",
Usage: "disable stack output", Usage: "disable stack output",
} }
EVMInterpreterFlag = cli.StringFlag{
Name: "vm.evm",
Usage: "External EVM configuration (default = built-in interpreter)",
Value: "",
}
) )
func init() { func init() {
@ -133,6 +138,7 @@ func init() {
ReceiverFlag, ReceiverFlag,
DisableMemoryFlag, DisableMemoryFlag,
DisableStackFlag, DisableStackFlag,
EVMInterpreterFlag,
} }
app.Commands = []cli.Command{ app.Commands = []cli.Command{
compileCommand, compileCommand,

View file

@ -89,7 +89,7 @@ func runCmd(ctx *cli.Context) error {
genesisConfig *core.Genesis genesisConfig *core.Genesis
) )
if ctx.GlobalBool(MachineFlag.Name) { if ctx.GlobalBool(MachineFlag.Name) {
tracer = NewJSONLogger(logconfig, os.Stdout) tracer = vm.NewJSONLogger(logconfig, os.Stdout)
} else if ctx.GlobalBool(DebugFlag.Name) { } else if ctx.GlobalBool(DebugFlag.Name) {
debugLogger = vm.NewStructLogger(logconfig) debugLogger = vm.NewStructLogger(logconfig)
tracer = debugLogger tracer = debugLogger
@ -173,6 +173,7 @@ func runCmd(ctx *cli.Context) error {
EVMConfig: vm.Config{ EVMConfig: vm.Config{
Tracer: tracer, Tracer: tracer,
Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name), Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
EVMInterpreter: ctx.GlobalString(EVMInterpreterFlag.Name),
}, },
} }
@ -206,6 +207,7 @@ func runCmd(ctx *cli.Context) error {
execTime := time.Since(tstart) execTime := time.Since(tstart)
if ctx.GlobalBool(DumpFlag.Name) { if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit(true)
statedb.IntermediateRoot(true) statedb.IntermediateRoot(true)
fmt.Println(string(statedb.Dump())) fmt.Println(string(statedb.Dump()))
} }

View file

@ -68,7 +68,7 @@ func stateTestCmd(ctx *cli.Context) error {
) )
switch { switch {
case ctx.GlobalBool(MachineFlag.Name): case ctx.GlobalBool(MachineFlag.Name):
tracer = NewJSONLogger(config, os.Stderr) tracer = vm.NewJSONLogger(config, os.Stderr)
case ctx.GlobalBool(DebugFlag.Name): case ctx.GlobalBool(DebugFlag.Name):
debugger = vm.NewStructLogger(config) debugger = vm.NewStructLogger(config)

View file

@ -256,7 +256,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
} }
for _, boot := range enodes { for _, boot := range enodes {
old, err := enode.ParseV4(boot.String()) old, err := enode.ParseV4(boot.String())
if err != nil { if err == nil {
stack.Server().AddPeer(old) stack.Server().AddPeer(old)
} }
} }

View file

@ -20,7 +20,7 @@ import (
"bufio" "bufio"
"errors" "errors"
"fmt" "fmt"
"io" "math/big"
"os" "os"
"reflect" "reflect"
"unicode" "unicode"
@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/dashboard" "github.com/ethereum/go-ethereum/dashboard"
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/graphql"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
@ -124,6 +125,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
} }
// Apply flags. // Apply flags.
utils.SetULC(ctx, &cfg.Eth)
utils.SetNodeConfig(ctx, &cfg.Node) utils.SetNodeConfig(ctx, &cfg.Node)
stack, err := node.New(&cfg.Node) stack, err := node.New(&cfg.Node)
if err != nil { if err != nil {
@ -152,7 +154,9 @@ func enableWhisper(ctx *cli.Context) bool {
func makeFullNode(ctx *cli.Context) *node.Node { func makeFullNode(ctx *cli.Context) *node.Node {
stack, cfg := makeConfigNode(ctx) stack, cfg := makeConfigNode(ctx)
if ctx.GlobalIsSet(utils.ConstantinopleOverrideFlag.Name) {
cfg.Eth.ConstantinopleOverride = new(big.Int).SetUint64(ctx.GlobalUint64(utils.ConstantinopleOverrideFlag.Name))
}
utils.RegisterEthService(stack, &cfg.Eth) utils.RegisterEthService(stack, &cfg.Eth)
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) { if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
@ -174,6 +178,13 @@ func makeFullNode(ctx *cli.Context) *node.Node {
utils.RegisterShhService(stack, &cfg.Shh) utils.RegisterShhService(stack, &cfg.Shh)
} }
// Configure GraphQL if required
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
if err := graphql.RegisterGraphQLService(stack, cfg.Node.GraphQLEndpoint(), cfg.Node.GraphQLCors, cfg.Node.GraphQLVirtualHosts, cfg.Node.HTTPTimeouts); err != nil {
utils.Fatalf("Failed to register the Ethereum service: %v", err)
}
}
// Add the Ethereum Stats daemon if requested. // Add the Ethereum Stats daemon if requested.
if cfg.Ethstats.URL != "" { if cfg.Ethstats.URL != "" {
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL) utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
@ -195,7 +206,17 @@ func dumpConfig(ctx *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
io.WriteString(os.Stdout, comment)
os.Stdout.Write(out) dump := os.Stdout
if ctx.NArg() > 0 {
dump, err = os.OpenFile(ctx.Args().Get(0), os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
if err != nil {
return err
}
defer dump.Close()
}
dump.WriteString(comment)
dump.Write(out)
return nil return nil
} }

View file

@ -84,12 +84,17 @@ var (
utils.TxPoolAccountQueueFlag, utils.TxPoolAccountQueueFlag,
utils.TxPoolGlobalQueueFlag, utils.TxPoolGlobalQueueFlag,
utils.TxPoolLifetimeFlag, utils.TxPoolLifetimeFlag,
utils.ULCModeConfigFlag,
utils.OnlyAnnounceModeFlag,
utils.ULCTrustedNodesFlag,
utils.ULCMinTrustedFractionFlag,
utils.SyncModeFlag, utils.SyncModeFlag,
utils.ExitWhenSyncedFlag, utils.ExitWhenSyncedFlag,
utils.GCModeFlag, utils.GCModeFlag,
utils.LightServFlag, utils.LightServFlag,
utils.LightPeersFlag, utils.LightPeersFlag,
utils.LightKDFFlag, utils.LightKDFFlag,
utils.WhitelistFlag,
utils.CacheFlag, utils.CacheFlag,
utils.CacheDatabaseFlag, utils.CacheDatabaseFlag,
utils.CacheTrieFlag, utils.CacheTrieFlag,
@ -125,6 +130,7 @@ var (
utils.RinkebyFlag, utils.RinkebyFlag,
utils.VMEnableDebugFlag, utils.VMEnableDebugFlag,
utils.NetworkIdFlag, utils.NetworkIdFlag,
utils.ConstantinopleOverrideFlag,
utils.RPCCORSDomainFlag, utils.RPCCORSDomainFlag,
utils.RPCVirtualHostsFlag, utils.RPCVirtualHostsFlag,
utils.EthStatsURLFlag, utils.EthStatsURLFlag,
@ -142,6 +148,11 @@ var (
utils.RPCEnabledFlag, utils.RPCEnabledFlag,
utils.RPCListenAddrFlag, utils.RPCListenAddrFlag,
utils.RPCPortFlag, utils.RPCPortFlag,
utils.GraphQLEnabledFlag,
utils.GraphQLListenAddrFlag,
utils.GraphQLPortFlag,
utils.GraphQLCORSDomainFlag,
utils.GraphQLVirtualHostsFlag,
utils.RPCApiFlag, utils.RPCApiFlag,
utils.WSEnabledFlag, utils.WSEnabledFlag,
utils.WSListenAddrFlag, utils.WSListenAddrFlag,
@ -173,7 +184,7 @@ func init() {
// Initialize the CLI app and start Geth // Initialize the CLI app and start Geth
app.Action = geth app.Action = geth
app.HideVersion = true // we have a command to print the version app.HideVersion = true // we have a command to print the version
app.Copyright = "Copyright 2013-2018 The go-ethereum Authors" app.Copyright = "Copyright 2013-2019 The go-ethereum Authors"
app.Commands = []cli.Command{ app.Commands = []cli.Command{
// See chaincmd.go: // See chaincmd.go:
initCommand, initCommand,

View file

@ -33,7 +33,7 @@ import (
var AppHelpTemplate = `NAME: var AppHelpTemplate = `NAME:
{{.App.Name}} - {{.App.Usage}} {{.App.Name}} - {{.App.Usage}}
Copyright 2013-2018 The go-ethereum Authors Copyright 2013-2019 The go-ethereum Authors
USAGE: USAGE:
{{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}} {{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
@ -82,6 +82,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.LightServFlag, utils.LightServFlag,
utils.LightPeersFlag, utils.LightPeersFlag,
utils.LightKDFFlag, utils.LightKDFFlag,
utils.WhitelistFlag,
}, },
}, },
{ {

View file

@ -20,35 +20,41 @@ import (
"encoding/binary" "encoding/binary"
"errors" "errors"
"math" "math"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
math2 "github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/ethash" "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
) )
// cppEthereumGenesisSpec represents the genesis specification format used by the // alethGenesisSpec represents the genesis specification format used by the
// C++ Ethereum implementation. // C++ Ethereum implementation.
type cppEthereumGenesisSpec struct { type alethGenesisSpec struct {
SealEngine string `json:"sealEngine"` SealEngine string `json:"sealEngine"`
Params struct { Params struct {
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"` AccountStartNonce math2.HexOrDecimal64 `json:"accountStartNonce"`
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"` HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"`
DaoHardforkBlock math2.HexOrDecimal64 `json:"daoHardforkBlock"`
EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"` EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"`
EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"` EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"`
ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"` ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"`
ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"` ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"`
NetworkID hexutil.Uint64 `json:"networkID"`
ChainID hexutil.Uint64 `json:"chainID"`
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
MinGasLimit hexutil.Uint64 `json:"minGasLimit"` MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"` MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"`
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"` TieBreakingGas bool `json:"tieBreakingGas"`
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"` MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"` DifficultyBoundDivisor *math2.HexOrDecimal256 `json:"difficultyBoundDivisor"`
DurationLimit *hexutil.Big `json:"durationLimit"` DurationLimit *math2.HexOrDecimal256 `json:"durationLimit"`
BlockReward *hexutil.Big `json:"blockReward"` BlockReward *hexutil.Big `json:"blockReward"`
NetworkID hexutil.Uint64 `json:"networkID"`
ChainID hexutil.Uint64 `json:"chainID"`
AllowFutureBlocks bool `json:"allowFutureBlocks"`
} `json:"params"` } `json:"params"`
Genesis struct { Genesis struct {
@ -62,57 +68,68 @@ type cppEthereumGenesisSpec struct {
GasLimit hexutil.Uint64 `json:"gasLimit"` GasLimit hexutil.Uint64 `json:"gasLimit"`
} `json:"genesis"` } `json:"genesis"`
Accounts map[common.Address]*cppEthereumGenesisSpecAccount `json:"accounts"` Accounts map[common.UnprefixedAddress]*alethGenesisSpecAccount `json:"accounts"`
} }
// cppEthereumGenesisSpecAccount is the prefunded genesis account and/or precompiled // alethGenesisSpecAccount is the prefunded genesis account and/or precompiled
// contract definition. // contract definition.
type cppEthereumGenesisSpecAccount struct { type alethGenesisSpecAccount struct {
Balance *hexutil.Big `json:"balance"` Balance *math2.HexOrDecimal256 `json:"balance"`
Nonce uint64 `json:"nonce,omitempty"` Nonce uint64 `json:"nonce,omitempty"`
Precompiled *cppEthereumGenesisSpecBuiltin `json:"precompiled,omitempty"` Precompiled *alethGenesisSpecBuiltin `json:"precompiled,omitempty"`
} }
// cppEthereumGenesisSpecBuiltin is the precompiled contract definition. // alethGenesisSpecBuiltin is the precompiled contract definition.
type cppEthereumGenesisSpecBuiltin struct { type alethGenesisSpecBuiltin struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"` StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"`
Linear *cppEthereumGenesisSpecLinearPricing `json:"linear,omitempty"` Linear *alethGenesisSpecLinearPricing `json:"linear,omitempty"`
} }
type cppEthereumGenesisSpecLinearPricing struct { type alethGenesisSpecLinearPricing struct {
Base uint64 `json:"base"` Base uint64 `json:"base"`
Word uint64 `json:"word"` Word uint64 `json:"word"`
} }
// newCppEthereumGenesisSpec converts a go-ethereum genesis block into a Parity specific // newAlethGenesisSpec converts a go-ethereum genesis block into a Aleth-specific
// chain specification format. // chain specification format.
func newCppEthereumGenesisSpec(network string, genesis *core.Genesis) (*cppEthereumGenesisSpec, error) { func newAlethGenesisSpec(network string, genesis *core.Genesis) (*alethGenesisSpec, error) {
// Only ethash is currently supported between go-ethereum and cpp-ethereum // Only ethash is currently supported between go-ethereum and aleth
if genesis.Config.Ethash == nil { if genesis.Config.Ethash == nil {
return nil, errors.New("unsupported consensus engine") return nil, errors.New("unsupported consensus engine")
} }
// Reconstruct the chain spec in Parity's format // Reconstruct the chain spec in Aleth format
spec := &cppEthereumGenesisSpec{ spec := &alethGenesisSpec{
SealEngine: "Ethash", SealEngine: "Ethash",
} }
// Some defaults
spec.Params.AccountStartNonce = 0 spec.Params.AccountStartNonce = 0
spec.Params.TieBreakingGas = false
spec.Params.AllowFutureBlocks = false
spec.Params.DaoHardforkBlock = 0
spec.Params.HomesteadForkBlock = (hexutil.Uint64)(genesis.Config.HomesteadBlock.Uint64()) spec.Params.HomesteadForkBlock = (hexutil.Uint64)(genesis.Config.HomesteadBlock.Uint64())
spec.Params.EIP150ForkBlock = (hexutil.Uint64)(genesis.Config.EIP150Block.Uint64()) spec.Params.EIP150ForkBlock = (hexutil.Uint64)(genesis.Config.EIP150Block.Uint64())
spec.Params.EIP158ForkBlock = (hexutil.Uint64)(genesis.Config.EIP158Block.Uint64()) spec.Params.EIP158ForkBlock = (hexutil.Uint64)(genesis.Config.EIP158Block.Uint64())
spec.Params.ByzantiumForkBlock = (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())
spec.Params.ConstantinopleForkBlock = (hexutil.Uint64)(math.MaxUint64) // Byzantium
if num := genesis.Config.ByzantiumBlock; num != nil {
spec.setByzantium(num)
}
// Constantinople
if num := genesis.Config.ConstantinopleBlock; num != nil {
spec.setConstantinople(num)
}
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize) spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit) spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxUint64) spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxInt64)
spec.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty) spec.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
spec.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor) spec.Params.DifficultyBoundDivisor = (*math2.HexOrDecimal256)(params.DifficultyBoundDivisor)
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor) spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
spec.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit) spec.Params.DurationLimit = (*math2.HexOrDecimal256)(params.DurationLimit)
spec.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward) spec.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
spec.Genesis.Nonce = (hexutil.Bytes)(make([]byte, 8)) spec.Genesis.Nonce = (hexutil.Bytes)(make([]byte, 8))
@ -126,77 +143,109 @@ func newCppEthereumGenesisSpec(network string, genesis *core.Genesis) (*cppEther
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData) spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit) spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
spec.Accounts = make(map[common.Address]*cppEthereumGenesisSpecAccount)
for address, account := range genesis.Alloc { for address, account := range genesis.Alloc {
spec.Accounts[address] = &cppEthereumGenesisSpecAccount{ spec.setAccount(address, account)
Balance: (*hexutil.Big)(account.Balance),
Nonce: account.Nonce,
}
}
spec.Accounts[common.BytesToAddress([]byte{1})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "ecrecover", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 3000},
}
spec.Accounts[common.BytesToAddress([]byte{2})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "sha256", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 60, Word: 12},
}
spec.Accounts[common.BytesToAddress([]byte{3})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "ripemd160", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 600, Word: 120},
}
spec.Accounts[common.BytesToAddress([]byte{4})].Precompiled = &cppEthereumGenesisSpecBuiltin{
Name: "identity", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 15, Word: 3},
} }
spec.setPrecompile(1, &alethGenesisSpecBuiltin{Name: "ecrecover",
Linear: &alethGenesisSpecLinearPricing{Base: 3000}})
spec.setPrecompile(2, &alethGenesisSpecBuiltin{Name: "sha256",
Linear: &alethGenesisSpecLinearPricing{Base: 60, Word: 12}})
spec.setPrecompile(3, &alethGenesisSpecBuiltin{Name: "ripemd160",
Linear: &alethGenesisSpecLinearPricing{Base: 600, Word: 120}})
spec.setPrecompile(4, &alethGenesisSpecBuiltin{Name: "identity",
Linear: &alethGenesisSpecLinearPricing{Base: 15, Word: 3}})
if genesis.Config.ByzantiumBlock != nil { if genesis.Config.ByzantiumBlock != nil {
spec.Accounts[common.BytesToAddress([]byte{5})].Precompiled = &cppEthereumGenesisSpecBuiltin{ spec.setPrecompile(5, &alethGenesisSpecBuiltin{Name: "modexp",
Name: "modexp", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())})
} spec.setPrecompile(6, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_add",
spec.Accounts[common.BytesToAddress([]byte{6})].Precompiled = &cppEthereumGenesisSpecBuiltin{ StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
Name: "alt_bn128_G1_add", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 500}, Linear: &alethGenesisSpecLinearPricing{Base: 500}})
} spec.setPrecompile(7, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_mul",
spec.Accounts[common.BytesToAddress([]byte{7})].Precompiled = &cppEthereumGenesisSpecBuiltin{ StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
Name: "alt_bn128_G1_mul", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 40000}, Linear: &alethGenesisSpecLinearPricing{Base: 40000}})
} spec.setPrecompile(8, &alethGenesisSpecBuiltin{Name: "alt_bn128_pairing_product",
spec.Accounts[common.BytesToAddress([]byte{8})].Precompiled = &cppEthereumGenesisSpecBuiltin{ StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())})
Name: "alt_bn128_pairing_product", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
}
} }
return spec, nil return spec, nil
} }
func (spec *alethGenesisSpec) setPrecompile(address byte, data *alethGenesisSpecBuiltin) {
if spec.Accounts == nil {
spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount)
}
addr := common.UnprefixedAddress(common.BytesToAddress([]byte{address}))
if _, exist := spec.Accounts[addr]; !exist {
spec.Accounts[addr] = &alethGenesisSpecAccount{}
}
spec.Accounts[addr].Precompiled = data
}
func (spec *alethGenesisSpec) setAccount(address common.Address, account core.GenesisAccount) {
if spec.Accounts == nil {
spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount)
}
a, exist := spec.Accounts[common.UnprefixedAddress(address)]
if !exist {
a = &alethGenesisSpecAccount{}
spec.Accounts[common.UnprefixedAddress(address)] = a
}
a.Balance = (*math2.HexOrDecimal256)(account.Balance)
a.Nonce = account.Nonce
}
func (spec *alethGenesisSpec) setByzantium(num *big.Int) {
spec.Params.ByzantiumForkBlock = hexutil.Uint64(num.Uint64())
}
func (spec *alethGenesisSpec) setConstantinople(num *big.Int) {
spec.Params.ConstantinopleForkBlock = hexutil.Uint64(num.Uint64())
}
// parityChainSpec is the chain specification format used by Parity. // parityChainSpec is the chain specification format used by Parity.
type parityChainSpec struct { type parityChainSpec struct {
Name string `json:"name"` Name string `json:"name"`
Datadir string `json:"dataDir"`
Engine struct { Engine struct {
Ethash struct { Ethash struct {
Params struct { Params struct {
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"` MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"` DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
DurationLimit *hexutil.Big `json:"durationLimit"` DurationLimit *hexutil.Big `json:"durationLimit"`
BlockReward *hexutil.Big `json:"blockReward"` BlockReward map[string]string `json:"blockReward"`
HomesteadTransition uint64 `json:"homesteadTransition"` DifficultyBombDelays map[string]string `json:"difficultyBombDelays"`
EIP150Transition uint64 `json:"eip150Transition"` HomesteadTransition hexutil.Uint64 `json:"homesteadTransition"`
EIP160Transition uint64 `json:"eip160Transition"` EIP100bTransition hexutil.Uint64 `json:"eip100bTransition"`
EIP161abcTransition uint64 `json:"eip161abcTransition"`
EIP161dTransition uint64 `json:"eip161dTransition"`
EIP649Reward *hexutil.Big `json:"eip649Reward"`
EIP100bTransition uint64 `json:"eip100bTransition"`
EIP649Transition uint64 `json:"eip649Transition"`
} `json:"params"` } `json:"params"`
} `json:"Ethash"` } `json:"Ethash"`
} `json:"engine"` } `json:"engine"`
Params struct { Params struct {
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"` MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
MinGasLimit hexutil.Uint64 `json:"minGasLimit"` MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"` GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
NetworkID hexutil.Uint64 `json:"networkID"` NetworkID hexutil.Uint64 `json:"networkID"`
MaxCodeSize uint64 `json:"maxCodeSize"` ChainID hexutil.Uint64 `json:"chainID"`
EIP155Transition uint64 `json:"eip155Transition"` MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"`
EIP98Transition uint64 `json:"eip98Transition"` MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"`
EIP86Transition uint64 `json:"eip86Transition"` EIP98Transition hexutil.Uint64 `json:"eip98Transition"`
EIP140Transition uint64 `json:"eip140Transition"` EIP150Transition hexutil.Uint64 `json:"eip150Transition"`
EIP211Transition uint64 `json:"eip211Transition"` EIP160Transition hexutil.Uint64 `json:"eip160Transition"`
EIP214Transition uint64 `json:"eip214Transition"` EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"`
EIP658Transition uint64 `json:"eip658Transition"` EIP161dTransition hexutil.Uint64 `json:"eip161dTransition"`
EIP155Transition hexutil.Uint64 `json:"eip155Transition"`
EIP140Transition hexutil.Uint64 `json:"eip140Transition"`
EIP211Transition hexutil.Uint64 `json:"eip211Transition"`
EIP214Transition hexutil.Uint64 `json:"eip214Transition"`
EIP658Transition hexutil.Uint64 `json:"eip658Transition"`
EIP145Transition hexutil.Uint64 `json:"eip145Transition"`
EIP1014Transition hexutil.Uint64 `json:"eip1014Transition"`
EIP1052Transition hexutil.Uint64 `json:"eip1052Transition"`
EIP1283Transition hexutil.Uint64 `json:"eip1283Transition"`
EIP1283DisableTransition hexutil.Uint64 `json:"eip1283DisableTransition"`
} `json:"params"` } `json:"params"`
Genesis struct { Genesis struct {
@ -216,21 +265,21 @@ type parityChainSpec struct {
} `json:"genesis"` } `json:"genesis"`
Nodes []string `json:"nodes"` Nodes []string `json:"nodes"`
Accounts map[common.Address]*parityChainSpecAccount `json:"accounts"` Accounts map[common.UnprefixedAddress]*parityChainSpecAccount `json:"accounts"`
} }
// parityChainSpecAccount is the prefunded genesis account and/or precompiled // parityChainSpecAccount is the prefunded genesis account and/or precompiled
// contract definition. // contract definition.
type parityChainSpecAccount struct { type parityChainSpecAccount struct {
Balance *hexutil.Big `json:"balance"` Balance math2.HexOrDecimal256 `json:"balance"`
Nonce uint64 `json:"nonce,omitempty"` Nonce math2.HexOrDecimal64 `json:"nonce,omitempty"`
Builtin *parityChainSpecBuiltin `json:"builtin,omitempty"` Builtin *parityChainSpecBuiltin `json:"builtin,omitempty"`
} }
// parityChainSpecBuiltin is the precompiled contract definition. // parityChainSpecBuiltin is the precompiled contract definition.
type parityChainSpecBuiltin struct { type parityChainSpecBuiltin struct {
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
ActivateAt uint64 `json:"activate_at,omitempty"` ActivateAt math2.HexOrDecimal64 `json:"activate_at,omitempty"`
Pricing *parityChainSpecPricing `json:"pricing,omitempty"` Pricing *parityChainSpecPricing `json:"pricing,omitempty"`
} }
@ -267,32 +316,54 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
spec := &parityChainSpec{ spec := &parityChainSpec{
Name: network, Name: network,
Nodes: bootnodes, Nodes: bootnodes,
Datadir: strings.ToLower(network),
} }
spec.Engine.Ethash.Params.BlockReward = make(map[string]string)
spec.Engine.Ethash.Params.DifficultyBombDelays = make(map[string]string)
// Frontier
spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty) spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor) spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit) spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
spec.Engine.Ethash.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward) spec.Engine.Ethash.Params.BlockReward["0x0"] = hexutil.EncodeBig(ethash.FrontierBlockReward)
spec.Engine.Ethash.Params.HomesteadTransition = genesis.Config.HomesteadBlock.Uint64()
spec.Engine.Ethash.Params.EIP150Transition = genesis.Config.EIP150Block.Uint64() // Homestead
spec.Engine.Ethash.Params.EIP160Transition = genesis.Config.EIP155Block.Uint64() spec.Engine.Ethash.Params.HomesteadTransition = hexutil.Uint64(genesis.Config.HomesteadBlock.Uint64())
spec.Engine.Ethash.Params.EIP161abcTransition = genesis.Config.EIP158Block.Uint64()
spec.Engine.Ethash.Params.EIP161dTransition = genesis.Config.EIP158Block.Uint64() // Tangerine Whistle : 150
spec.Engine.Ethash.Params.EIP649Reward = (*hexutil.Big)(ethash.ByzantiumBlockReward) // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-608.md
spec.Engine.Ethash.Params.EIP100bTransition = genesis.Config.ByzantiumBlock.Uint64() spec.Params.EIP150Transition = hexutil.Uint64(genesis.Config.EIP150Block.Uint64())
spec.Engine.Ethash.Params.EIP649Transition = genesis.Config.ByzantiumBlock.Uint64()
// Spurious Dragon: 155, 160, 161, 170
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-607.md
spec.Params.EIP155Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64())
spec.Params.EIP160Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64())
spec.Params.EIP161abcTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64())
spec.Params.EIP161dTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64())
// Byzantium
if num := genesis.Config.ByzantiumBlock; num != nil {
spec.setByzantium(num)
}
// Constantinople
if num := genesis.Config.ConstantinopleBlock; num != nil {
spec.setConstantinople(num)
}
// ConstantinopleFix (remove eip-1283)
if num := genesis.Config.PetersburgBlock; num != nil {
spec.setConstantinopleFix(num)
}
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize) spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit) spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor) spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64()) spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
spec.Params.MaxCodeSize = params.MaxCodeSize spec.Params.MaxCodeSize = params.MaxCodeSize
spec.Params.EIP155Transition = genesis.Config.EIP155Block.Uint64() // geth has it set from zero
spec.Params.EIP98Transition = math.MaxUint64 spec.Params.MaxCodeSizeTransition = 0
spec.Params.EIP86Transition = math.MaxUint64
spec.Params.EIP140Transition = genesis.Config.ByzantiumBlock.Uint64() // Disable this one
spec.Params.EIP211Transition = genesis.Config.ByzantiumBlock.Uint64() spec.Params.EIP98Transition = math.MaxInt64
spec.Params.EIP214Transition = genesis.Config.ByzantiumBlock.Uint64()
spec.Params.EIP658Transition = genesis.Config.ByzantiumBlock.Uint64()
spec.Genesis.Seal.Ethereum.Nonce = (hexutil.Bytes)(make([]byte, 8)) spec.Genesis.Seal.Ethereum.Nonce = (hexutil.Bytes)(make([]byte, 8))
binary.LittleEndian.PutUint64(spec.Genesis.Seal.Ethereum.Nonce[:], genesis.Nonce) binary.LittleEndian.PutUint64(spec.Genesis.Seal.Ethereum.Nonce[:], genesis.Nonce)
@ -305,42 +376,81 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData) spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit) spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
spec.Accounts = make(map[common.Address]*parityChainSpecAccount) spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount)
for address, account := range genesis.Alloc { for address, account := range genesis.Alloc {
spec.Accounts[address] = &parityChainSpecAccount{ bal := math2.HexOrDecimal256(*account.Balance)
Balance: (*hexutil.Big)(account.Balance),
Nonce: account.Nonce, spec.Accounts[common.UnprefixedAddress(address)] = &parityChainSpecAccount{
Balance: bal,
Nonce: math2.HexOrDecimal64(account.Nonce),
} }
} }
spec.Accounts[common.BytesToAddress([]byte{1})].Builtin = &parityChainSpecBuiltin{ spec.setPrecompile(1, &parityChainSpecBuiltin{Name: "ecrecover",
Name: "ecrecover", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}}, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}}})
}
spec.Accounts[common.BytesToAddress([]byte{2})].Builtin = &parityChainSpecBuiltin{ spec.setPrecompile(2, &parityChainSpecBuiltin{
Name: "sha256", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 60, Word: 12}}, Name: "sha256", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 60, Word: 12}},
} })
spec.Accounts[common.BytesToAddress([]byte{3})].Builtin = &parityChainSpecBuiltin{ spec.setPrecompile(3, &parityChainSpecBuiltin{
Name: "ripemd160", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 600, Word: 120}}, Name: "ripemd160", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 600, Word: 120}},
} })
spec.Accounts[common.BytesToAddress([]byte{4})].Builtin = &parityChainSpecBuiltin{ spec.setPrecompile(4, &parityChainSpecBuiltin{
Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}}, Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}},
} })
if genesis.Config.ByzantiumBlock != nil { if genesis.Config.ByzantiumBlock != nil {
spec.Accounts[common.BytesToAddress([]byte{5})].Builtin = &parityChainSpecBuiltin{ blnum := math2.HexOrDecimal64(genesis.Config.ByzantiumBlock.Uint64())
Name: "modexp", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}}, spec.setPrecompile(5, &parityChainSpecBuiltin{
} Name: "modexp", ActivateAt: blnum, Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}},
spec.Accounts[common.BytesToAddress([]byte{6})].Builtin = &parityChainSpecBuiltin{ })
Name: "alt_bn128_add", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}}, spec.setPrecompile(6, &parityChainSpecBuiltin{
} Name: "alt_bn128_add", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}},
spec.Accounts[common.BytesToAddress([]byte{7})].Builtin = &parityChainSpecBuiltin{ })
Name: "alt_bn128_mul", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}}, spec.setPrecompile(7, &parityChainSpecBuiltin{
} Name: "alt_bn128_mul", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}},
spec.Accounts[common.BytesToAddress([]byte{8})].Builtin = &parityChainSpecBuiltin{ })
Name: "alt_bn128_pairing", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}}, spec.setPrecompile(8, &parityChainSpecBuiltin{
} Name: "alt_bn128_pairing", ActivateAt: blnum, Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}},
})
} }
return spec, nil return spec, nil
} }
func (spec *parityChainSpec) setPrecompile(address byte, data *parityChainSpecBuiltin) {
if spec.Accounts == nil {
spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount)
}
a := common.UnprefixedAddress(common.BytesToAddress([]byte{address}))
if _, exist := spec.Accounts[a]; !exist {
spec.Accounts[a] = &parityChainSpecAccount{}
}
spec.Accounts[a].Builtin = data
}
func (spec *parityChainSpec) setByzantium(num *big.Int) {
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ByzantiumBlockReward)
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(3000000)
n := hexutil.Uint64(num.Uint64())
spec.Engine.Ethash.Params.EIP100bTransition = n
spec.Params.EIP140Transition = n
spec.Params.EIP211Transition = n
spec.Params.EIP214Transition = n
spec.Params.EIP658Transition = n
}
func (spec *parityChainSpec) setConstantinople(num *big.Int) {
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ConstantinopleBlockReward)
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(2000000)
n := hexutil.Uint64(num.Uint64())
spec.Params.EIP145Transition = n
spec.Params.EIP1014Transition = n
spec.Params.EIP1052Transition = n
spec.Params.EIP1283Transition = n
}
func (spec *parityChainSpec) setConstantinopleFix(num *big.Int) {
spec.Params.EIP1283DisableTransition = hexutil.Uint64(num.Uint64())
}
// pyEthereumGenesisSpec represents the genesis specification format used by the // pyEthereumGenesisSpec represents the genesis specification format used by the
// Python Ethereum implementation. // Python Ethereum implementation.
type pyEthereumGenesisSpec struct { type pyEthereumGenesisSpec struct {

109
cmd/puppeth/genesis_test.go Normal file
View file

@ -0,0 +1,109 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"reflect"
"strings"
"testing"
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/core"
)
// Tests the go-ethereum to Aleth chainspec conversion for the Stureby testnet.
func TestAlethSturebyConverter(t *testing.T) {
blob, err := ioutil.ReadFile("testdata/stureby_geth.json")
if err != nil {
t.Fatalf("could not read file: %v", err)
}
var genesis core.Genesis
if err := json.Unmarshal(blob, &genesis); err != nil {
t.Fatalf("failed parsing genesis: %v", err)
}
spec, err := newAlethGenesisSpec("stureby", &genesis)
if err != nil {
t.Fatalf("failed creating chainspec: %v", err)
}
expBlob, err := ioutil.ReadFile("testdata/stureby_aleth.json")
if err != nil {
t.Fatalf("could not read file: %v", err)
}
expspec := &alethGenesisSpec{}
if err := json.Unmarshal(expBlob, expspec); err != nil {
t.Fatalf("failed parsing genesis: %v", err)
}
if !reflect.DeepEqual(expspec, spec) {
t.Errorf("chainspec mismatch")
c := spew.ConfigState{
DisablePointerAddresses: true,
SortKeys: true,
}
exp := strings.Split(c.Sdump(expspec), "\n")
got := strings.Split(c.Sdump(spec), "\n")
for i := 0; i < len(exp) && i < len(got); i++ {
if exp[i] != got[i] {
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i])
}
}
}
}
// Tests the go-ethereum to Parity chainspec conversion for the Stureby testnet.
func TestParitySturebyConverter(t *testing.T) {
blob, err := ioutil.ReadFile("testdata/stureby_geth.json")
if err != nil {
t.Fatalf("could not read file: %v", err)
}
var genesis core.Genesis
if err := json.Unmarshal(blob, &genesis); err != nil {
t.Fatalf("failed parsing genesis: %v", err)
}
spec, err := newParityChainSpec("Stureby", &genesis, []string{})
if err != nil {
t.Fatalf("failed creating chainspec: %v", err)
}
expBlob, err := ioutil.ReadFile("testdata/stureby_parity.json")
if err != nil {
t.Fatalf("could not read file: %v", err)
}
expspec := &parityChainSpec{}
if err := json.Unmarshal(expBlob, expspec); err != nil {
t.Fatalf("failed parsing genesis: %v", err)
}
expspec.Nodes = []string{}
if !reflect.DeepEqual(expspec, spec) {
t.Errorf("chainspec mismatch")
c := spew.ConfigState{
DisablePointerAddresses: true,
SortKeys: true,
}
exp := strings.Split(c.Sdump(expspec), "\n")
got := strings.Split(c.Sdump(spec), "\n")
for i := 0; i < len(exp) && i < len(got); i++ {
if exp[i] != got[i] {
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i])
}
}
}
}

View file

@ -632,6 +632,7 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
"Spurious": conf.Genesis.Config.EIP155Block, "Spurious": conf.Genesis.Config.EIP155Block,
"Byzantium": conf.Genesis.Config.ByzantiumBlock, "Byzantium": conf.Genesis.Config.ByzantiumBlock,
"Constantinople": conf.Genesis.Config.ConstantinopleBlock, "Constantinople": conf.Genesis.Config.ConstantinopleBlock,
"ConstantinopleFix": conf.Genesis.Config.PetersburgBlock,
}) })
files[filepath.Join(workdir, "index.html")] = indexfile.Bytes() files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()
@ -640,7 +641,7 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
files[filepath.Join(workdir, network+".json")] = genesis files[filepath.Join(workdir, network+".json")] = genesis
if conf.Genesis.Config.Ethash != nil { if conf.Genesis.Config.Ethash != nil {
cppSpec, err := newCppEthereumGenesisSpec(network, conf.Genesis) cppSpec, err := newAlethGenesisSpec(network, conf.Genesis)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -43,7 +43,8 @@ version: '2'
services: services:
ethstats: ethstats:
build: . build: .
image: {{.Network}}/ethstats{{if not .VHost}} image: {{.Network}}/ethstats
container_name: {{.Network}}_ethstats_1{{if not .VHost}}
ports: ports:
- "{{.Port}}:3000"{{end}} - "{{.Port}}:3000"{{end}}
environment: environment:

View file

@ -77,6 +77,7 @@ services:
explorer: explorer:
build: . build: .
image: {{.Network}}/explorer image: {{.Network}}/explorer
container_name: {{.Network}}_explorer_1
ports: ports:
- "{{.NodePort}}:{{.NodePort}}" - "{{.NodePort}}:{{.NodePort}}"
- "{{.NodePort}}:{{.NodePort}}/udp"{{if not .VHost}} - "{{.NodePort}}:{{.NodePort}}/udp"{{if not .VHost}}

View file

@ -56,8 +56,10 @@ services:
faucet: faucet:
build: . build: .
image: {{.Network}}/faucet image: {{.Network}}/faucet
container_name: {{.Network}}_faucet_1
ports: ports:
- "{{.EthPort}}:{{.EthPort}}"{{if not .VHost}} - "{{.EthPort}}:{{.EthPort}}"
- "{{.EthPort}}:{{.EthPort}}/udp"{{if not .VHost}}
- "{{.ApiPort}}:8080"{{end}} - "{{.ApiPort}}:8080"{{end}}
volumes: volumes:
- {{.Datadir}}:/root/.faucet - {{.Datadir}}:/root/.faucet

View file

@ -40,6 +40,7 @@ services:
nginx: nginx:
build: . build: .
image: {{.Network}}/nginx image: {{.Network}}/nginx
container_name: {{.Network}}_nginx_1
ports: ports:
- "{{.Port}}:80" - "{{.Port}}:80"
volumes: volumes:

View file

@ -55,6 +55,7 @@ services:
{{.Type}}: {{.Type}}:
build: . build: .
image: {{.Network}}/{{.Type}} image: {{.Network}}/{{.Type}}
container_name: {{.Network}}_{{.Type}}_1
ports: ports:
- "{{.Port}}:{{.Port}}" - "{{.Port}}:{{.Port}}"
- "{{.Port}}:{{.Port}}/udp" - "{{.Port}}:{{.Port}}/udp"

View file

@ -57,6 +57,7 @@ services:
wallet: wallet:
build: . build: .
image: {{.Network}}/wallet image: {{.Network}}/wallet
container_name: {{.Network}}_wallet_1
ports: ports:
- "{{.NodePort}}:{{.NodePort}}" - "{{.NodePort}}:{{.NodePort}}"
- "{{.NodePort}}:{{.NodePort}}/udp" - "{{.NodePort}}:{{.NodePort}}/udp"

View file

@ -43,18 +43,23 @@ func main() {
Usage: "log level to emit to the screen", Usage: "log level to emit to the screen",
}, },
} }
app.Action = func(c *cli.Context) error { app.Before = func(c *cli.Context) error {
// Set up the logger to print everything and the random generator // Set up the logger to print everything and the random generator
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
rand.Seed(time.Now().UnixNano()) rand.Seed(time.Now().UnixNano())
network := c.String("network") return nil
if strings.Contains(network, " ") || strings.Contains(network, "-") { }
log.Crit("No spaces or hyphens allowed in network name") app.Action = runWizard
app.Run(os.Args)
}
// runWizard start the wizard and relinquish control to it.
func runWizard(c *cli.Context) error {
network := c.String("network")
if strings.Contains(network, " ") || strings.Contains(network, "-") || strings.ToLower(network) != network {
log.Crit("No spaces, hyphens or capital letters allowed in network name")
} }
// Start the wizard and relinquish control
makeWizard(c.String("network")).run() makeWizard(c.String("network")).run()
return nil return nil
} }
app.Run(os.Args)
}

112
cmd/puppeth/testdata/stureby_aleth.json vendored Normal file
View file

@ -0,0 +1,112 @@
{
"sealEngine":"Ethash",
"params":{
"accountStartNonce":"0x00",
"maximumExtraDataSize":"0x20",
"homesteadForkBlock":"0x2710",
"daoHardforkBlock":"0x00",
"EIP150ForkBlock":"0x3a98",
"EIP158ForkBlock":"0x59d8",
"byzantiumForkBlock":"0x7530",
"constantinopleForkBlock":"0x9c40",
"minGasLimit":"0x1388",
"maxGasLimit":"0x7fffffffffffffff",
"tieBreakingGas":false,
"gasLimitBoundDivisor":"0x0400",
"minimumDifficulty":"0x20000",
"difficultyBoundDivisor":"0x0800",
"durationLimit":"0x0d",
"blockReward":"0x4563918244F40000",
"networkID":"0x4cb2e",
"chainID":"0x4cb2e",
"allowFutureBlocks":false
},
"genesis":{
"nonce":"0x0000000000000000",
"difficulty":"0x20000",
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"author":"0x0000000000000000000000000000000000000000",
"timestamp":"0x59a4e76d",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
"gasLimit":"0x47b760"
},
"accounts":{
"0000000000000000000000000000000000000001":{
"balance":"1",
"precompiled":{
"name":"ecrecover",
"linear":{
"base":3000,
"word":0
}
}
},
"0000000000000000000000000000000000000002":{
"balance":"1",
"precompiled":{
"name":"sha256",
"linear":{
"base":60,
"word":12
}
}
},
"0000000000000000000000000000000000000003":{
"balance":"1",
"precompiled":{
"name":"ripemd160",
"linear":{
"base":600,
"word":120
}
}
},
"0000000000000000000000000000000000000004":{
"balance":"1",
"precompiled":{
"name":"identity",
"linear":{
"base":15,
"word":3
}
}
},
"0000000000000000000000000000000000000005":{
"balance":"1",
"precompiled":{
"name":"modexp",
"startingBlock":"0x7530"
}
},
"0000000000000000000000000000000000000006":{
"balance":"1",
"precompiled":{
"name":"alt_bn128_G1_add",
"startingBlock":"0x7530",
"linear":{
"base":500,
"word":0
}
}
},
"0000000000000000000000000000000000000007":{
"balance":"1",
"precompiled":{
"name":"alt_bn128_G1_mul",
"startingBlock":"0x7530",
"linear":{
"base":40000,
"word":0
}
}
},
"0000000000000000000000000000000000000008":{
"balance":"1",
"precompiled":{
"name":"alt_bn128_pairing_product",
"startingBlock":"0x7530"
}
}
}
}

47
cmd/puppeth/testdata/stureby_geth.json vendored Normal file
View file

@ -0,0 +1,47 @@
{
"config": {
"ethash":{},
"chainId": 314158,
"homesteadBlock": 10000,
"eip150Block": 15000,
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"eip155Block": 23000,
"eip158Block": 23000,
"byzantiumBlock": 30000,
"constantinopleBlock": 40000
},
"nonce": "0x0",
"timestamp": "0x59a4e76d",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
"gasLimit": "0x47b760",
"difficulty": "0x20000",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"coinbase": "0x0000000000000000000000000000000000000000",
"alloc": {
"0000000000000000000000000000000000000001": {
"balance": "0x01"
},
"0000000000000000000000000000000000000002": {
"balance": "0x01"
},
"0000000000000000000000000000000000000003": {
"balance": "0x01"
},
"0000000000000000000000000000000000000004": {
"balance": "0x01"
},
"0000000000000000000000000000000000000005": {
"balance": "0x01"
},
"0000000000000000000000000000000000000006": {
"balance": "0x01"
},
"0000000000000000000000000000000000000007": {
"balance": "0x01"
},
"0000000000000000000000000000000000000008": {
"balance": "0x01"
}
}
}

181
cmd/puppeth/testdata/stureby_parity.json vendored Normal file
View file

@ -0,0 +1,181 @@
{
"name":"Stureby",
"dataDir":"stureby",
"engine":{
"Ethash":{
"params":{
"minimumDifficulty":"0x20000",
"difficultyBoundDivisor":"0x800",
"durationLimit":"0xd",
"blockReward":{
"0x0":"0x4563918244f40000",
"0x7530":"0x29a2241af62c0000",
"0x9c40":"0x1bc16d674ec80000"
},
"homesteadTransition":"0x2710",
"eip100bTransition":"0x7530",
"difficultyBombDelays":{
"0x7530":"0x2dc6c0",
"0x9c40":"0x1e8480"
}
}
}
},
"params":{
"accountStartNonce":"0x0",
"maximumExtraDataSize":"0x20",
"gasLimitBoundDivisor":"0x400",
"minGasLimit":"0x1388",
"networkID":"0x4cb2e",
"chainID":"0x4cb2e",
"maxCodeSize":"0x6000",
"maxCodeSizeTransition":"0x0",
"eip98Transition": "0x7fffffffffffffff",
"eip150Transition":"0x3a98",
"eip160Transition":"0x59d8",
"eip161abcTransition":"0x59d8",
"eip161dTransition":"0x59d8",
"eip155Transition":"0x59d8",
"eip140Transition":"0x7530",
"eip211Transition":"0x7530",
"eip214Transition":"0x7530",
"eip658Transition":"0x7530",
"eip145Transition":"0x9c40",
"eip1014Transition":"0x9c40",
"eip1052Transition":"0x9c40",
"eip1283Transition":"0x9c40"
},
"genesis":{
"seal":{
"ethereum":{
"nonce":"0x0000000000000000",
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty":"0x20000",
"author":"0x0000000000000000000000000000000000000000",
"timestamp":"0x59a4e76d",
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
"gasLimit":"0x47b760"
},
"nodes":[
"enode://dfa7aca3f5b635fbfe7d0b20575f25e40d9e27b4bfbb3cf74364a42023ad9f25c1a4383bcc8cced86ee511a7d03415345a4df05be37f1dff040e4c780699f1c0@168.61.153.255:31303",
"enode://ef441b20dd70aeabf0eac35c3b8a2854e5ce04db0e30be9152ea9fd129359dcbb3f803993303ff5781c755dfd7223f3fe43505f583cccb740949407677412ba9@40.74.91.252:31303",
"enode://953b5ea1c8987cf46008232a0160324fd00d41320ecf00e23af86ec8f5396b19eb57ddab37c78141be56f62e9077de4f4dfa0747fa768ed8c8531bbfb1046237@40.70.214.166:31303",
"enode://276e613dd4b277a66591e565711e6c8bb107f0905248a9f8f8228c1a87992e156e5114bb9937c02824a9d9d25f76340442cf86e2028bf5293cae19904fb2b98e@35.178.251.52:30303",
"enode://064c820d41e52ed7d426ac64b60506c2998235bedc7e67cb497c6faf7bb4fc54fe56fc82d0add3180b747c0c4f40a1108a6f84d7d0629ed606d504528e61cc57@3.8.5.3:30303",
"enode://90069fdabcc5e684fa5d59430bebbb12755d9362dfe5006a1485b13d71a78a3812d36e74dd7d88e50b51add01e097ea80f16263aeaa4f0230db6c79e2a97e7ca@217.29.191.142:30303",
"enode://0aac74b7fd28726275e466acb5e03bc88a95927e9951eb66b5efb239b2f798ada0690853b2f2823fe4efa408f0f3d4dd258430bc952a5ff70677b8625b3e3b14@40.115.33.57:40404",
"enode://0b96415a10f835106d83e090a0528eed5e7887e5c802a6d084e9f1993a9d0fc713781e6e4101f6365e9b91259712f291acc0a9e6e667e22023050d602c36fbe2@40.115.33.57:40414"
],
"accounts":{
"0000000000000000000000000000000000000001":{
"balance":"1",
"nonce":"0",
"builtin":{
"name":"ecrecover",
"pricing":{
"linear":{
"base":3000,
"word":0
}
}
}
},
"0000000000000000000000000000000000000002":{
"balance":"1",
"nonce":"0",
"builtin":{
"name":"sha256",
"pricing":{
"linear":{
"base":60,
"word":12
}
}
}
},
"0000000000000000000000000000000000000003":{
"balance":"1",
"nonce":"0",
"builtin":{
"name":"ripemd160",
"pricing":{
"linear":{
"base":600,
"word":120
}
}
}
},
"0000000000000000000000000000000000000004":{
"balance":"1",
"nonce":"0",
"builtin":{
"name":"identity",
"pricing":{
"linear":{
"base":15,
"word":3
}
}
}
},
"0000000000000000000000000000000000000005":{
"balance":"1",
"nonce":"0",
"builtin":{
"name":"modexp",
"activate_at":"0x7530",
"pricing":{
"modexp":{
"divisor":20
}
}
}
},
"0000000000000000000000000000000000000006":{
"balance":"1",
"nonce":"0",
"builtin":{
"name":"alt_bn128_add",
"activate_at":"0x7530",
"pricing":{
"linear":{
"base":500,
"word":0
}
}
}
},
"0000000000000000000000000000000000000007":{
"balance":"1",
"nonce":"0",
"builtin":{
"name":"alt_bn128_mul",
"activate_at":"0x7530",
"pricing":{
"linear":{
"base":40000,
"word":0
}
}
}
},
"0000000000000000000000000000000000000008":{
"balance":"1",
"nonce":"0",
"builtin":{
"name":"alt_bn128_pairing",
"activate_at":"0x7530",
"pricing":{
"alt_bn128_pairing":{
"base":100000,
"pair":80000
}
}
}
}
}
}

View file

@ -23,6 +23,7 @@ import (
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"net" "net"
"net/url"
"os" "os"
"path/filepath" "path/filepath"
"sort" "sort"
@ -118,6 +119,47 @@ func (w *wizard) readDefaultString(def string) string {
return def return def
} }
// readDefaultYesNo reads a single line from stdin, trimming if from spaces and
// interpreting it as a 'yes' or a 'no'. If an empty line is entered, the default
// value is returned.
func (w *wizard) readDefaultYesNo(def bool) bool {
for {
fmt.Printf("> ")
text, err := w.in.ReadString('\n')
if err != nil {
log.Crit("Failed to read user input", "err", err)
}
if text = strings.ToLower(strings.TrimSpace(text)); text == "" {
return def
}
if text == "y" || text == "yes" {
return true
}
if text == "n" || text == "no" {
return false
}
log.Error("Invalid input, expected 'y', 'yes', 'n', 'no' or empty")
}
}
// readURL reads a single line from stdin, trimming if from spaces and trying to
// interpret it as a URL (http, https or file).
func (w *wizard) readURL() *url.URL {
for {
fmt.Printf("> ")
text, err := w.in.ReadString('\n')
if err != nil {
log.Crit("Failed to read user input", "err", err)
}
uri, err := url.Parse(strings.TrimSpace(text))
if err != nil {
log.Error("Invalid input, expected URL", "err", err)
continue
}
return uri
}
}
// readInt reads a single line from stdin, trimming if from spaces, enforcing it // readInt reads a single line from stdin, trimming if from spaces, enforcing it
// to parse into an integer. // to parse into an integer.
func (w *wizard) readInt() int { func (w *wizard) readInt() int {

View file

@ -137,14 +137,14 @@ func (w *wizard) deployDashboard() {
if w.conf.ethstats != "" { if w.conf.ethstats != "" {
fmt.Println() fmt.Println()
fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)") fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)")
infos.trusted = w.readDefaultString("y") == "y" infos.trusted = w.readDefaultYesNo(true)
} }
// Try to deploy the dashboard container on the host // Try to deploy the dashboard container on the host
nocache := false nocache := false
if existed { if existed {
fmt.Println() fmt.Println()
fmt.Printf("Should the dashboard be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the dashboard be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultYesNo(false)
} }
if out, err := deployDashboard(client, w.network, &w.conf, infos, nocache); err != nil { if out, err := deployDashboard(client, w.network, &w.conf, infos, nocache); err != nil {
log.Error("Failed to deploy dashboard container", "err", err) log.Error("Failed to deploy dashboard container", "err", err)

View file

@ -67,11 +67,11 @@ func (w *wizard) deployEthstats() {
if existed { if existed {
fmt.Println() fmt.Println()
fmt.Printf("Keep existing IP %v blacklist (y/n)? (default = yes)\n", infos.banned) fmt.Printf("Keep existing IP %v blacklist (y/n)? (default = yes)\n", infos.banned)
if w.readDefaultString("y") != "y" { if !w.readDefaultYesNo(true) {
// The user might want to clear the entire list, although generally probably not // The user might want to clear the entire list, although generally probably not
fmt.Println() fmt.Println()
fmt.Printf("Clear out blacklist and start over (y/n)? (default = no)\n") fmt.Printf("Clear out blacklist and start over (y/n)? (default = no)\n")
if w.readDefaultString("n") != "n" { if w.readDefaultYesNo(false) {
infos.banned = nil infos.banned = nil
} }
// Offer the user to explicitly add/remove certain IP addresses // Offer the user to explicitly add/remove certain IP addresses
@ -106,7 +106,7 @@ func (w *wizard) deployEthstats() {
if existed { if existed {
fmt.Println() fmt.Println()
fmt.Printf("Should the ethstats be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the ethstats be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultYesNo(false)
} }
trusted := make([]string, 0, len(w.servers)) trusted := make([]string, 0, len(w.servers))
for _, client := range w.servers { for _, client := range w.servers {

View file

@ -100,7 +100,7 @@ func (w *wizard) deployExplorer() {
if existed { if existed {
fmt.Println() fmt.Println()
fmt.Printf("Should the explorer be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the explorer be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultYesNo(false)
} }
if out, err := deployExplorer(client, w.network, chain, infos, nocache); err != nil { if out, err := deployExplorer(client, w.network, chain, infos, nocache); err != nil {
log.Error("Failed to deploy explorer container", "err", err) log.Error("Failed to deploy explorer container", "err", err)

View file

@ -81,7 +81,7 @@ func (w *wizard) deployFaucet() {
if infos.captchaToken != "" { if infos.captchaToken != "" {
fmt.Println() fmt.Println()
fmt.Println("Reuse previous reCaptcha API authorization (y/n)? (default = yes)") fmt.Println("Reuse previous reCaptcha API authorization (y/n)? (default = yes)")
if w.readDefaultString("y") != "y" { if !w.readDefaultYesNo(true) {
infos.captchaToken, infos.captchaSecret = "", "" infos.captchaToken, infos.captchaSecret = "", ""
} }
} }
@ -89,7 +89,7 @@ func (w *wizard) deployFaucet() {
// No previous authorization (or old one discarded) // No previous authorization (or old one discarded)
fmt.Println() fmt.Println()
fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)") fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)")
if w.readDefaultString("n") == "n" { if !w.readDefaultYesNo(false) {
log.Warn("Users will be able to requests funds via automated scripts") log.Warn("Users will be able to requests funds via automated scripts")
} else { } else {
// Captcha protection explicitly requested, read the site and secret keys // Captcha protection explicitly requested, read the site and secret keys
@ -132,7 +132,7 @@ func (w *wizard) deployFaucet() {
} else { } else {
fmt.Println() fmt.Println()
fmt.Printf("Reuse previous (%s) funding account (y/n)? (default = yes)\n", key.Address.Hex()) fmt.Printf("Reuse previous (%s) funding account (y/n)? (default = yes)\n", key.Address.Hex())
if w.readDefaultString("y") != "y" { if !w.readDefaultYesNo(true) {
infos.node.keyJSON, infos.node.keyPass = "", "" infos.node.keyJSON, infos.node.keyPass = "", ""
} }
} }
@ -166,7 +166,7 @@ func (w *wizard) deployFaucet() {
if existed { if existed {
fmt.Println() fmt.Println()
fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultYesNo(false)
} }
if out, err := deployFaucet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil { if out, err := deployFaucet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
log.Error("Failed to deploy faucet container", "err", err) log.Error("Failed to deploy faucet container", "err", err)

View file

@ -20,9 +20,13 @@ import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
"math/rand" "math/rand"
"net/http"
"os"
"path/filepath"
"time" "time"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -45,6 +49,7 @@ func (w *wizard) makeGenesis() {
EIP155Block: big.NewInt(3), EIP155Block: big.NewInt(3),
EIP158Block: big.NewInt(3), EIP158Block: big.NewInt(3),
ByzantiumBlock: big.NewInt(4), ByzantiumBlock: big.NewInt(4),
ConstantinopleBlock: big.NewInt(5),
}, },
} }
// Figure out which consensus engine to choose // Figure out which consensus engine to choose
@ -114,10 +119,14 @@ func (w *wizard) makeGenesis() {
} }
break break
} }
fmt.Println()
fmt.Println("Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)")
if w.readDefaultYesNo(true) {
// Add a batch of precompile balances to avoid them getting deleted // Add a batch of precompile balances to avoid them getting deleted
for i := int64(0); i < 256; i++ { for i := int64(0); i < 256; i++ {
genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)} genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)}
} }
}
// Query the user for some custom extras // Query the user for some custom extras
fmt.Println() fmt.Println()
fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)") fmt.Println("Specify your chain/network ID if you want an explicit one (default = random)")
@ -130,53 +139,134 @@ func (w *wizard) makeGenesis() {
w.conf.flush() w.conf.flush()
} }
// importGenesis imports a Geth genesis spec into puppeth.
func (w *wizard) importGenesis() {
// Request the genesis JSON spec URL from the user
fmt.Println()
fmt.Println("Where's the genesis file? (local file or http/https url)")
url := w.readURL()
// Convert the various allowed URLs to a reader stream
var reader io.Reader
switch url.Scheme {
case "http", "https":
// Remote web URL, retrieve it via an HTTP client
res, err := http.Get(url.String())
if err != nil {
log.Error("Failed to retrieve remote genesis", "err", err)
return
}
defer res.Body.Close()
reader = res.Body
case "":
// Schemaless URL, interpret as a local file
file, err := os.Open(url.String())
if err != nil {
log.Error("Failed to open local genesis", "err", err)
return
}
defer file.Close()
reader = file
default:
log.Error("Unsupported genesis URL scheme", "scheme", url.Scheme)
return
}
// Parse the genesis file and inject it successful
var genesis core.Genesis
if err := json.NewDecoder(reader).Decode(&genesis); err != nil {
log.Error("Invalid genesis spec: %v", err)
return
}
log.Info("Imported genesis block")
w.conf.Genesis = &genesis
w.conf.flush()
}
// manageGenesis permits the modification of chain configuration parameters in // manageGenesis permits the modification of chain configuration parameters in
// a genesis config and the export of the entire genesis spec. // a genesis config and the export of the entire genesis spec.
func (w *wizard) manageGenesis() { func (w *wizard) manageGenesis() {
// Figure out whether to modify or export the genesis // Figure out whether to modify or export the genesis
fmt.Println() fmt.Println()
fmt.Println(" 1. Modify existing fork rules") fmt.Println(" 1. Modify existing fork rules")
fmt.Println(" 2. Export genesis configuration") fmt.Println(" 2. Export genesis configurations")
fmt.Println(" 3. Remove genesis configuration") fmt.Println(" 3. Remove genesis configuration")
choice := w.read() choice := w.read()
switch { switch choice {
case choice == "1": case "1":
// Fork rule updating requested, iterate over each fork // Fork rule updating requested, iterate over each fork
fmt.Println() fmt.Println()
fmt.Printf("Which block should Homestead come into effect? (default = %v)\n", w.conf.Genesis.Config.HomesteadBlock) fmt.Printf("Which block should Homestead come into effect? (default = %v)\n", w.conf.Genesis.Config.HomesteadBlock)
w.conf.Genesis.Config.HomesteadBlock = w.readDefaultBigInt(w.conf.Genesis.Config.HomesteadBlock) w.conf.Genesis.Config.HomesteadBlock = w.readDefaultBigInt(w.conf.Genesis.Config.HomesteadBlock)
fmt.Println() fmt.Println()
fmt.Printf("Which block should EIP150 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block) fmt.Printf("Which block should EIP150 (Tangerine Whistle) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block)
w.conf.Genesis.Config.EIP150Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP150Block) w.conf.Genesis.Config.EIP150Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP150Block)
fmt.Println() fmt.Println()
fmt.Printf("Which block should EIP155 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block) fmt.Printf("Which block should EIP155 (Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block)
w.conf.Genesis.Config.EIP155Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP155Block) w.conf.Genesis.Config.EIP155Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP155Block)
fmt.Println() fmt.Println()
fmt.Printf("Which block should EIP158 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block) fmt.Printf("Which block should EIP158/161 (also Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block)
w.conf.Genesis.Config.EIP158Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP158Block) w.conf.Genesis.Config.EIP158Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP158Block)
fmt.Println() fmt.Println()
fmt.Printf("Which block should Byzantium come into effect? (default = %v)\n", w.conf.Genesis.Config.ByzantiumBlock) fmt.Printf("Which block should Byzantium come into effect? (default = %v)\n", w.conf.Genesis.Config.ByzantiumBlock)
w.conf.Genesis.Config.ByzantiumBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ByzantiumBlock) w.conf.Genesis.Config.ByzantiumBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ByzantiumBlock)
fmt.Println()
fmt.Printf("Which block should Constantinople come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
w.conf.Genesis.Config.ConstantinopleBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
fmt.Println()
fmt.Printf("Which block should Constantinople-Fix (remove EIP-1283) come into effect? (default = %v)\n", w.conf.Genesis.Config.ConstantinopleBlock)
w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ConstantinopleBlock)
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ") out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
fmt.Printf("Chain configuration updated:\n\n%s\n", out) fmt.Printf("Chain configuration updated:\n\n%s\n", out)
case choice == "2": case "2":
// Save whatever genesis configuration we currently have // Save whatever genesis configuration we currently have
fmt.Println() fmt.Println()
fmt.Printf("Which file to save the genesis into? (default = %s.json)\n", w.network) fmt.Printf("Which folder to save the genesis specs into? (default = current)\n")
out, _ := json.MarshalIndent(w.conf.Genesis, "", " ") fmt.Printf(" Will create %s.json, %s-aleth.json, %s-harmony.json, %s-parity.json\n", w.network, w.network, w.network, w.network)
if err := ioutil.WriteFile(w.readDefaultString(fmt.Sprintf("%s.json", w.network)), out, 0644); err != nil {
log.Error("Failed to save genesis file", "err", err)
}
log.Info("Exported existing genesis block")
case choice == "3": folder := w.readDefaultString(".")
if err := os.MkdirAll(folder, 0755); err != nil {
log.Error("Failed to create spec folder", "folder", folder, "err", err)
return
}
out, _ := json.MarshalIndent(w.conf.Genesis, "", " ")
// Export the native genesis spec used by puppeth and Geth
gethJson := filepath.Join(folder, fmt.Sprintf("%s.json", w.network))
if err := ioutil.WriteFile((gethJson), out, 0644); err != nil {
log.Error("Failed to save genesis file", "err", err)
return
}
log.Info("Saved native genesis chain spec", "path", gethJson)
// Export the genesis spec used by Aleth (formerly C++ Ethereum)
if spec, err := newAlethGenesisSpec(w.network, w.conf.Genesis); err != nil {
log.Error("Failed to create Aleth chain spec", "err", err)
} else {
saveGenesis(folder, w.network, "aleth", spec)
}
// Export the genesis spec used by Parity
if spec, err := newParityChainSpec(w.network, w.conf.Genesis, []string{}); err != nil {
log.Error("Failed to create Parity chain spec", "err", err)
} else {
saveGenesis(folder, w.network, "parity", spec)
}
// Export the genesis spec used by Harmony (formerly EthereumJ
saveGenesis(folder, w.network, "harmony", w.conf.Genesis)
case "3":
// Make sure we don't have any services running // Make sure we don't have any services running
if len(w.conf.servers()) > 0 { if len(w.conf.servers()) > 0 {
log.Error("Genesis reset requires all services and servers torn down") log.Error("Genesis reset requires all services and servers torn down")
@ -186,8 +276,20 @@ func (w *wizard) manageGenesis() {
w.conf.Genesis = nil w.conf.Genesis = nil
w.conf.flush() w.conf.flush()
default: default:
log.Error("That's not something I can do") log.Error("That's not something I can do")
return
} }
} }
// saveGenesis JSON encodes an arbitrary genesis spec into a pre-defined file.
func saveGenesis(folder, network, client string, spec interface{}) {
path := filepath.Join(folder, fmt.Sprintf("%s-%s.json", network, client))
out, _ := json.Marshal(spec)
if err := ioutil.WriteFile(path, out, 0644); err != nil {
log.Error("Failed to save genesis file", "client", client, "err", err)
return
}
log.Info("Saved genesis chain spec", "client", client, "path", path)
}

View file

@ -61,14 +61,14 @@ func (w *wizard) run() {
// Make sure we have a good network name to work with fmt.Println() // Make sure we have a good network name to work with fmt.Println()
// Docker accepts hyphens in image names, but doesn't like it for container names // Docker accepts hyphens in image names, but doesn't like it for container names
if w.network == "" { if w.network == "" {
fmt.Println("Please specify a network name to administer (no spaces or hyphens, please)") fmt.Println("Please specify a network name to administer (no spaces, hyphens or capital letters please)")
for { for {
w.network = w.readString() w.network = w.readString()
if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") { if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") && strings.ToLower(w.network) == w.network {
fmt.Printf("\nSweet, you can set this via --network=%s next time!\n\n", w.network) fmt.Printf("\nSweet, you can set this via --network=%s next time!\n\n", w.network)
break break
} }
log.Error("I also like to live dangerously, still no spaces or hyphens") log.Error("I also like to live dangerously, still no spaces, hyphens or capital letters")
} }
} }
log.Info("Administering Ethereum network", "name", w.network) log.Info("Administering Ethereum network", "name", w.network)
@ -131,7 +131,20 @@ func (w *wizard) run() {
case choice == "2": case choice == "2":
if w.conf.Genesis == nil { if w.conf.Genesis == nil {
fmt.Println()
fmt.Println("What would you like to do? (default = create)")
fmt.Println(" 1. Create new genesis from scratch")
fmt.Println(" 2. Import already existing genesis")
choice := w.read()
switch {
case choice == "" || choice == "1":
w.makeGenesis() w.makeGenesis()
case choice == "2":
w.importGenesis()
default:
log.Error("That's not something I can do")
}
} else { } else {
w.manageGenesis() w.manageGenesis()
} }
@ -149,7 +162,6 @@ func (w *wizard) run() {
} else { } else {
w.manageComponents() w.manageComponents()
} }
default: default:
log.Error("That's not something I can do") log.Error("That's not something I can do")
} }

View file

@ -41,12 +41,12 @@ func (w *wizard) ensureVirtualHost(client *sshClient, port int, def string) (str
// Reverse proxy is not running, offer to deploy a new one // Reverse proxy is not running, offer to deploy a new one
fmt.Println() fmt.Println()
fmt.Println("Allow sharing the port with other services (y/n)? (default = yes)") fmt.Println("Allow sharing the port with other services (y/n)? (default = yes)")
if w.readDefaultString("y") == "y" { if w.readDefaultYesNo(true) {
nocache := false nocache := false
if proxy != nil { if proxy != nil {
fmt.Println() fmt.Println()
fmt.Printf("Should the reverse-proxy be rebuilt from scratch (y/n)? (default = no)\n") fmt.Printf("Should the reverse-proxy be rebuilt from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultYesNo(false)
} }
if out, err := deployNginx(client, w.network, port, nocache); err != nil { if out, err := deployNginx(client, w.network, port, nocache); err != nil {
log.Error("Failed to deploy reverse-proxy", "err", err) log.Error("Failed to deploy reverse-proxy", "err", err)

View file

@ -126,7 +126,7 @@ func (w *wizard) deployNode(boot bool) {
} else { } else {
fmt.Println() fmt.Println()
fmt.Printf("Reuse previous (%s) signing account (y/n)? (default = yes)\n", key.Address.Hex()) fmt.Printf("Reuse previous (%s) signing account (y/n)? (default = yes)\n", key.Address.Hex())
if w.readDefaultString("y") != "y" { if !w.readDefaultYesNo(true) {
infos.keyJSON, infos.keyPass = "", "" infos.keyJSON, infos.keyPass = "", ""
} }
} }
@ -165,7 +165,7 @@ func (w *wizard) deployNode(boot bool) {
if existed { if existed {
fmt.Println() fmt.Println()
fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultYesNo(false)
} }
if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil { if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
log.Error("Failed to deploy Ethereum node container", "err", err) log.Error("Failed to deploy Ethereum node container", "err", err)

View file

@ -96,7 +96,7 @@ func (w *wizard) deployWallet() {
if existed { if existed {
fmt.Println() fmt.Println()
fmt.Printf("Should the wallet be built from scratch (y/n)? (default = no)\n") fmt.Printf("Should the wallet be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n" nocache = w.readDefaultYesNo(false)
} }
if out, err := deployWallet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil { if out, err := deployWallet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
log.Error("Failed to deploy wallet container", "err", err) log.Error("Failed to deploy wallet container", "err", err)

View file

@ -33,11 +33,11 @@ import (
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies" "github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/swarm/api" "github.com/ethereum/go-ethereum/swarm/api"
swarmapi "github.com/ethereum/go-ethereum/swarm/api/client" swarmapi "github.com/ethereum/go-ethereum/swarm/api/client"
"github.com/ethereum/go-ethereum/swarm/testutil" "github.com/ethereum/go-ethereum/swarm/testutil"
"golang.org/x/crypto/sha3"
) )
const ( const (
@ -598,7 +598,7 @@ func TestKeypairSanity(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
hasher := sha3.NewKeccak256() hasher := sha3.NewLegacyKeccak256()
hasher.Write(salt) hasher.Write(salt)
shared, err := hex.DecodeString(sharedSecret) shared, err := hex.DecodeString(sharedSecret)
if err != nil { if err != nil {

View file

@ -17,61 +17,8 @@
package main package main
var SwarmBootnodes = []string{ var SwarmBootnodes = []string{
// Foundation Swarm Gateway Cluster // EF Swarm Bootnode - AWS - eu-central-1
"enode://e5c6f9215c919a5450a7b8c14c22535607b69f2c8e1e7f6f430cb25d7a2c27cd1df4c4f18ad7c1d7e5162e271ffcd3f20b1a1467fb6e790e7d727f3b2193de97@52.232.7.187:30399", "enode://4c113504601930bf2000c29bcd98d1716b6167749f58bad703bae338332fe93cc9d9204f08afb44100dc7bea479205f5d162df579f9a8f76f8b402d339709023@3.122.203.99:30301",
"enode://9b2fe07e69ccc7db5fef15793dab7d7d2e697ed92132d6e9548218e68a34613a8671ad03a6658d862b468ed693cae8a0f8f8d37274e4a657ffb59ca84676e45b@52.232.7.187:30400", // EF Swarm Bootnode - AWS - us-west-2
"enode://76c1059162c93ef9df0f01097c824d17c492634df211ef4c806935b349082233b63b90c23970254b3b7138d630400f7cf9b71e80355a446a8b733296cb04169a@52.232.7.187:30401", "enode://89f2ede3371bff1ad9f2088f2012984e280287a4e2b68007c2a6ad994909c51886b4a8e9e2ecc97f9910aca538398e0a5804b0ee80a187fde1ba4f32626322ba@52.35.212.179:30301",
"enode://ce46bbe2a8263145d65252d52da06e000ad350ed09c876a71ea9544efa42f63c1e1b6cc56307373aaad8f9dd069c90d0ed2dd1530106200e16f4ca681dd8ae2d@52.232.7.187:30402",
"enode://f431e0d6008a6c35c6e670373d828390c8323e53da8158e7bfc43cf07e632cc9e472188be8df01decadea2d4a068f1428caba769b632554a8fb0607bc296988f@52.232.7.187:30403",
"enode://174720abfff83d7392f121108ae50ea54e04889afe020df883655c0f6cb95414db945a0228d8982fe000d86fc9f4b7669161adc89cd7cd56f78f01489ab2b99b@52.232.7.187:30404",
"enode://2ae89be4be61a689b6f9ecee4360a59e185e010ab750f14b63b4ae43d4180e872e18e3437d4386ce44875dc7cc6eb761acba06412fe3178f3dac1dab3b65703e@52.232.7.187:30405",
"enode://24abebe1c0e6d75d6052ce3219a87be8573fd6397b4cb51f0773b83abba9b3d872bfb273cdc07389715b87adfac02f5235f5241442c5089802cbd8d42e310fce@52.232.7.187:30406",
"enode://d08dfa46bfbbdbcaafbb6e34abee4786610f6c91e0b76d7881f0334ac10dda41d8c1f2b6eedffb4493293c335c0ad46776443b2208d1fbbb9e1a90b25ee4eef2@52.232.7.187:30407",
"enode://8d95eb0f837d27581a43668ed3b8783d69dc4e84aa3edd7a0897e026155c8f59c8702fdc0375ee7bac15757c9c78e1315d9b73e4ce59c936db52ea4ae2f501c7@52.232.7.187:30408",
"enode://a5967cc804aebd422baaaba9f06f27c9e695ccab335b61088130f8cbe64e3cdf78793868c7051dfc06eecfe844fad54bc7f6dfaed9db3c7ecef279cb829c25fb@52.232.7.187:30409",
"enode://5f00134d81a8f2ebcc46f8766f627f492893eda48138f811b7de2168308171968f01710bca6da05764e74f14bae41652f554e6321f1aed85fa3461e89d075dbf@52.232.7.187:30410",
"enode://b2142b79b01a5aa66a5e23cc35e78219a8e97bc2412a6698cee24ae02e87078b725d71730711bd62e25ff1aa8658c6633778af8ac14c63814a337c3dd0ebda9f@52.232.7.187:30411",
"enode://1ffa7651094867d6486ce3ef46d27a052c2cb968b618346c6df7040322c7efc3337547ba85d4cbba32e8b31c42c867202554735c06d4c664b9afada2ed0c4b3c@52.232.7.187:30412",
"enode://129e0c3d5f5df12273754f6f703d2424409fa4baa599e0b758c55600169313887855e75b082028d2302ec034b303898cd697cc7ae8256ba924ce927510da2c8d@52.232.7.187:30413",
"enode://419e2dc0d2f5b022cf16b0e28842658284909fa027a0fbbb5e2b755e7f846ea02a8f0b66a7534981edf6a7bcf8a14855344c6668e2cd4476ccd35a11537c9144@52.232.7.187:30414",
"enode://23d55ad900583231b91f2f62e3f72eb498b342afd58b682be3af052eed62b5651094471065981de33d8786f075f05e3cca499503b0ac8ae84b2a06e99f5b0723@52.232.7.187:30415",
"enode://bc56e4158c00e9f616d7ea533def20a89bef959df4e62a768ff238ff4e1e9223f57ecff969941c20921bad98749baae311c0fbebce53bf7bbb9d3dc903640990@52.232.7.187:30416",
"enode://433ce15199c409875e7e72fffd69fdafe746f17b20f0d5555281722a65fde6c80328fab600d37d8624509adc072c445ce0dad4a1c01cff6acf3132c11d429d4d@52.232.7.187:30417",
"enode://632ee95b8f0eac51ef89ceb29313fef3a60050181d66a6b125583b1a225a7694b252edc016efb58aa3b251da756cb73280842a022c658ed405223b2f58626343@52.232.7.187:30418",
"enode://4a0f9bcff7a4b9ee453fb298d0fb222592efe121512e30cd72fef631beb8c6a15153a1456eb073ee18551c0e003c569651a101892dc4124e90b933733a498bb5@52.232.7.187:30419",
"enode://f0d80fbc72d16df30e19aac3051eb56a7aff0c8367686702e01ea132d8b0b3ee00cadd6a859d2cca98ec68d3d574f8a8a87dba2347ec1e2818dc84bc3fa34fae@52.232.7.187:30420",
"enode://a199146906e4f9f2b94b195a8308d9a59a3564b92efaab898a4243fe4c2ad918b7a8e4853d9d901d94fad878270a2669d644591299c3d43de1b298c00b92b4a7@52.232.7.187:30421",
"enode://052036ea8736b37adbfb684d90ce43e11b3591b51f31489d7c726b03618dea4f73b1e659deb928e6bf40564edcdcf08351643f42db3d4ca1c2b5db95dad59e94@52.232.7.187:30422",
"enode://460e2b8c6da8f12fac96c836e7d108f4b7ec55a1c64631bb8992339e117e1c28328fee83af863196e20af1487a655d13e5ceba90e980e92502d5bac5834c1f71@52.232.7.187:30423",
"enode://6d2cdd13741b2e72e9031e1b93c6d9a4e68de2844aa4e939f6a8a8498a7c1d7e2ee4c64217e92a6df08c9a32c6764d173552810ef1bd2ecb356532d389dd2136@52.232.7.187:30424",
"enode://62105fc25ce2cd5b299647f47eaa9211502dc76f0e9f461df915782df7242ac3223e3db04356ae6ed2977ccac20f0b16864406e9ca514a40a004cb6a5d0402aa@52.232.7.187:30425",
"enode://e0e388fc520fd493c33f0ce16685e6f98fb6aec28f2edc14ee6b179594ee519a896425b0025bb6f0e182dd3e468443f19c70885fbc66560d000093a668a86aa8@52.232.7.187:30426",
"enode://63f3353a72521ea10022127a4fe6b4acbef197c3fe668fd9f4805542d8a6fcf79f6335fbab62d180a35e19b739483e740858b113fdd7c13a26ad7b4e318a5aef@52.232.7.187:30427",
"enode://33a42b927085678d4aefd4e70b861cfca6ef5f6c143696c4f755973fd29e64c9e658cad57a66a687a7a156da1e3688b1fbdd17bececff2ee009fff038fa5666b@52.232.7.187:30428",
"enode://259ab5ab5c1daee3eab7e3819ab3177b82d25c29e6c2444fdd3f956e356afae79a72840ccf2d0665fe82c81ebc3b3734da1178ac9fd5d62c67e674b69f86b6be@52.232.7.187:30429",
"enode://558bccad7445ce3fd8db116ed6ab4aed1324fdbdac2348417340c1764dc46d46bffe0728e5b7d5c36f12e794c289f18f57f08f085d2c65c9910a5c7a65b6a66a@52.232.7.187:30430",
"enode://abe60937a0657ffded718e3f84a32987286983be257bdd6004775c4b525747c2b598f4fac49c8de324de5ce75b22673fa541a7ce2d555fb7f8ca325744ae3577@52.232.7.187:30431",
"enode://bce6f0aaa5b230742680084df71d4f026b3eff7f564265599216a1b06b765303fdc9325de30ffd5dfdaf302ce4b14322891d2faea50ce2ca298d7409f5858339@52.232.7.187:30432",
"enode://21b957c4e03277d42be6660730ec1b93f540764f26c6abdb54d006611139c7081248486206dfbf64fcaffd62589e9c6b8ea77a5297e4b21a605f1bcf49483ed0@52.232.7.187:30433",
"enode://ff104e30e64f24c3d7328acee8b13354e5551bc8d60bb25ecbd9632d955c7e34bb2d969482d173355baad91c8282f8b592624eb3929151090da3b4448d4d58fb@52.232.7.187:30434",
"enode://c76e2b5f81a521bceaec1518926a21380a345df9cf463461562c6845795512497fb67679e155fc96a74350f8b78de8f4c135dd52b106dbbb9795452021d09ea5@52.232.7.187:30435",
"enode://3288fd860105164f3e9b69934c4eb18f7146cfab31b5a671f994e21a36e9287766e5f9f075aefbc404538c77f7c2eb2a4495020a7633a1c3970d94e9fa770aeb@52.232.7.187:30436",
"enode://6cea859c7396d46b20cfcaa80f9a11cd112f8684f2f782f7b4c0e1e0af9212113429522075101923b9b957603e6c32095a6a07b5e5e35183c521952ee108dfaf@52.232.7.187:30437",
"enode://f628ec56e4ca8317cc24cc4ac9b27b95edcce7b96e1c7f3b53e30de4a8580fe44f2f0694a513bdb0a431acaf2824074d6ace4690247bbc34c14f426af8c056ea@52.232.7.187:30438",
"enode://055ec8b26fc105c4f97970a1cce9773a5e34c03f511b839db742198a1c571e292c54aa799e9afb991cc8a560529b8cdf3e0c344bc6c282aff2f68eec59361ddf@52.232.7.187:30439",
"enode://48cb0d430c328974226aa33a931d8446cd5a8d40f3ead8f4ce7ad60faa1278192eb6d58bed91258d63e81f255fc107eec2425ce2ae8b22350dd556076e160610@52.232.7.187:30440",
"enode://3fadb7af7f770d5ffc6b073b8d42834bebb18ce1fe8a4fe270d2b799e7051327093960dc61d9a18870db288f7746a0e6ea2a013cd6ab0e5f97ca08199473aace@52.232.7.187:30441",
"enode://a5d7168024c9992769cf380ffa559a64b4f39a29d468f579559863814eb0ae0ed689ac0871a3a2b4c78b03297485ec322d578281131ef5d5c09a4beb6200a97a@52.232.7.187:30442",
"enode://9c57744c5b2c2d71abcbe80512652f9234d4ab041b768a2a886ab390fe6f184860f40e113290698652d7e20a8ac74d27ac8671db23eb475b6c5e6253e4693bf8@52.232.7.187:30443",
"enode://daca9ff0c3176045a0e0ed228dee00ec86bc0939b135dc6b1caa23745d20fd0332e1ee74ad04020e89df56c7146d831a91b89d15ca3df05ba7618769fefab376@52.232.7.187:30444",
"enode://a3f6af59428cb4b9acb198db15ef5554fa43c2b0c18e468a269722d64a27218963a2975eaf82750b6262e42192b5e3669ea51337b4cda62b33987981bc5e0c1a@52.232.7.187:30445",
"enode://fe571422fa4651c3354c85dac61911a6a6520dd3c0332967a49d4133ca30e16a8a4946fa73ca2cb5de77917ea701a905e1c3015b2f4defcd53132b61cc84127a@52.232.7.187:30446",
// Mainframe
"enode://ee9a5a571ea6c8a59f9a8bb2c569c865e922b41c91d09b942e8c1d4dd2e1725bd2c26149da14de1f6321a2c6fdf1e07c503c3e093fb61696daebf74d6acd916b@54.186.219.160:30399",
"enode://a03f0562ecb8a992ad5242345535e73483cdc18ab934d36bf24b567d43447c2cea68f89f1d51d504dd13acc30f24ebce5a150bea2ccb1b722122ce4271dc199d@52.67.248.147:30399",
"enode://e2cbf9eafd85903d3b1c56743035284320695e0072bc8d7396e0542aa5e1c321b236f67eab66b79c2f15d4447fa4bbe74dd67d0467da23e7eb829f60ec8a812b@13.58.169.1:30399",
"enode://8b8c6bda6047f1cad9fab2db4d3d02b7aa26279902c32879f7bcd4a7d189fee77fdc36ee151ce6b84279b4792e72578fd529d2274d014132465758fbfee51cee@13.209.13.15:30399",
"enode://63f6a8818927e429585287cf2ca0cb9b11fa990b7b9b331c2962cdc6f21807a2473b26e8256225c26caff70d7218e59586d704d49061452c6852e382c885d03c@35.154.106.174:30399",
"enode://ed4bd3b794ed73f18e6dcc70c6624dfec63b5654f6ab54e8f40b16eff8afbd342d4230e099ddea40e84423f81b2d2ea79799dc345257b1fec6f6c422c9d008f7@52.213.20.99:30399",
} }

View file

@ -79,6 +79,7 @@ const (
SWARM_ENV_STORE_PATH = "SWARM_STORE_PATH" SWARM_ENV_STORE_PATH = "SWARM_STORE_PATH"
SWARM_ENV_STORE_CAPACITY = "SWARM_STORE_CAPACITY" SWARM_ENV_STORE_CAPACITY = "SWARM_STORE_CAPACITY"
SWARM_ENV_STORE_CACHE_CAPACITY = "SWARM_STORE_CACHE_CAPACITY" SWARM_ENV_STORE_CACHE_CAPACITY = "SWARM_STORE_CACHE_CAPACITY"
SWARM_ENV_BOOTNODE_MODE = "SWARM_BOOTNODE_MODE"
SWARM_ACCESS_PASSWORD = "SWARM_ACCESS_PASSWORD" SWARM_ACCESS_PASSWORD = "SWARM_ACCESS_PASSWORD"
SWARM_AUTO_DEFAULTPATH = "SWARM_AUTO_DEFAULTPATH" SWARM_AUTO_DEFAULTPATH = "SWARM_AUTO_DEFAULTPATH"
GETH_ENV_DATADIR = "GETH_DATADIR" GETH_ENV_DATADIR = "GETH_DATADIR"
@ -164,10 +165,9 @@ func configFileOverride(config *bzzapi.Config, ctx *cli.Context) (*bzzapi.Config
return config, err return config, err
} }
//override the current config with whatever is provided through the command line // cmdLineOverride overrides the current config with whatever is provided through the command line
// most values are not allowed a zero value (empty string), if not otherwise noted // most values are not allowed a zero value (empty string), if not otherwise noted
func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Config { func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Config {
if keyid := ctx.GlobalString(SwarmAccountFlag.Name); keyid != "" { if keyid := ctx.GlobalString(SwarmAccountFlag.Name); keyid != "" {
currentConfig.BzzAccount = keyid currentConfig.BzzAccount = keyid
} }
@ -258,14 +258,17 @@ func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Con
currentConfig.LocalStoreParams.CacheCapacity = storeCacheCapacity currentConfig.LocalStoreParams.CacheCapacity = storeCacheCapacity
} }
if ctx.GlobalIsSet(SwarmBootnodeModeFlag.Name) {
currentConfig.BootnodeMode = ctx.GlobalBool(SwarmBootnodeModeFlag.Name)
}
return currentConfig return currentConfig
} }
//override the current config with whatver is provided in environment variables // envVarsOverride overrides the current config with whatver is provided in environment variables
// most values are not allowed a zero value (empty string), if not otherwise noted // most values are not allowed a zero value (empty string), if not otherwise noted
func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) { func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
if keyid := os.Getenv(SWARM_ENV_ACCOUNT); keyid != "" { if keyid := os.Getenv(SWARM_ENV_ACCOUNT); keyid != "" {
currentConfig.BzzAccount = keyid currentConfig.BzzAccount = keyid
} }
@ -364,6 +367,14 @@ func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
currentConfig.Cors = cors currentConfig.Cors = cors
} }
if bm := os.Getenv(SWARM_ENV_BOOTNODE_MODE); bm != "" {
bootnodeMode, err := strconv.ParseBool(bm)
if err != nil {
utils.Fatalf("invalid environment variable %s: %v", SWARM_ENV_BOOTNODE_MODE, err)
}
currentConfig.BootnodeMode = bootnodeMode
}
return currentConfig return currentConfig
} }

View file

@ -26,14 +26,14 @@ import (
"testing" "testing"
"time" "time"
"github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm" "github.com/ethereum/go-ethereum/swarm"
"github.com/ethereum/go-ethereum/swarm/api" "github.com/ethereum/go-ethereum/swarm/api"
"github.com/docker/docker/pkg/reexec"
) )
func TestDumpConfig(t *testing.T) { func TestConfigDump(t *testing.T) {
swarm := runSwarm(t, "dumpconfig") swarm := runSwarm(t, "dumpconfig")
defaultConf := api.NewConfig() defaultConf := api.NewConfig()
out, err := tomlSettings.Marshal(&defaultConf) out, err := tomlSettings.Marshal(&defaultConf)
@ -91,8 +91,8 @@ func TestConfigCmdLineOverrides(t *testing.T) {
fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(), fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(),
fmt.Sprintf("--%s", SwarmDeliverySkipCheckFlag.Name), fmt.Sprintf("--%s", SwarmDeliverySkipCheckFlag.Name),
fmt.Sprintf("--%s", EnsAPIFlag.Name), "", fmt.Sprintf("--%s", EnsAPIFlag.Name), "",
"--datadir", dir, fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir,
"--ipcpath", conf.IPCPath, fmt.Sprintf("--%s", utils.IPCPathFlag.Name), conf.IPCPath,
} }
node.Cmd = runSwarm(t, flags...) node.Cmd = runSwarm(t, flags...)
node.Cmd.InputLine(testPassphrase) node.Cmd.InputLine(testPassphrase)
@ -189,9 +189,9 @@ func TestConfigFileOverrides(t *testing.T) {
flags := []string{ flags := []string{
fmt.Sprintf("--%s", SwarmTomlConfigPathFlag.Name), f.Name(), fmt.Sprintf("--%s", SwarmTomlConfigPathFlag.Name), f.Name(),
fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(), fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(),
"--ens-api", "", fmt.Sprintf("--%s", EnsAPIFlag.Name), "",
"--ipcpath", conf.IPCPath, fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir,
"--datadir", dir, fmt.Sprintf("--%s", utils.IPCPathFlag.Name), conf.IPCPath,
} }
node.Cmd = runSwarm(t, flags...) node.Cmd = runSwarm(t, flags...)
node.Cmd.InputLine(testPassphrase) node.Cmd.InputLine(testPassphrase)
@ -407,9 +407,9 @@ func TestConfigCmdLineOverridesFile(t *testing.T) {
fmt.Sprintf("--%s", SwarmSyncDisabledFlag.Name), fmt.Sprintf("--%s", SwarmSyncDisabledFlag.Name),
fmt.Sprintf("--%s", SwarmTomlConfigPathFlag.Name), f.Name(), fmt.Sprintf("--%s", SwarmTomlConfigPathFlag.Name), f.Name(),
fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(), fmt.Sprintf("--%s", SwarmAccountFlag.Name), account.Address.String(),
"--ens-api", "", fmt.Sprintf("--%s", EnsAPIFlag.Name), "",
"--datadir", dir, fmt.Sprintf("--%s", utils.DataDirFlag.Name), dir,
"--ipcpath", conf.IPCPath, fmt.Sprintf("--%s", utils.IPCPathFlag.Name), conf.IPCPath,
} }
node.Cmd = runSwarm(t, flags...) node.Cmd = runSwarm(t, flags...)
node.Cmd.InputLine(testPassphrase) node.Cmd.InputLine(testPassphrase)
@ -466,7 +466,7 @@ func TestConfigCmdLineOverridesFile(t *testing.T) {
node.Shutdown() node.Shutdown()
} }
func TestValidateConfig(t *testing.T) { func TestConfigValidate(t *testing.T) {
for _, c := range []struct { for _, c := range []struct {
cfg *api.Config cfg *api.Config
err string err string

View file

@ -169,7 +169,6 @@ func feedUpdate(ctx *cli.Context) {
query = new(feed.Query) query = new(feed.Query)
query.User = signer.Address() query.User = signer.Address()
query.Topic = getTopic(ctx) query.Topic = getTopic(ctx)
} }
// Retrieve a feed update request // Retrieve a feed update request
@ -178,6 +177,11 @@ func feedUpdate(ctx *cli.Context) {
utils.Fatalf("Error retrieving feed status: %s", err.Error()) utils.Fatalf("Error retrieving feed status: %s", err.Error())
} }
// Check that the provided signer matches the request to sign
if updateRequest.User != signer.Address() {
utils.Fatalf("Signer address does not match the update request")
}
// set the new data // set the new data
updateRequest.SetData(data) updateRequest.SetData(data)

View file

@ -19,7 +19,6 @@ package main
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"io/ioutil" "io/ioutil"
"os" "os"
"testing" "testing"
@ -69,7 +68,7 @@ func TestCLIFeedUpdate(t *testing.T) {
hexData} hexData}
// create an update and expect an exit without errors // create an update and expect an exit without errors
log.Info(fmt.Sprintf("updating a feed with 'swarm feed update'")) log.Info("updating a feed with 'swarm feed update'")
cmd := runSwarm(t, flags...) cmd := runSwarm(t, flags...)
cmd.ExpectExit() cmd.ExpectExit()
@ -116,7 +115,7 @@ func TestCLIFeedUpdate(t *testing.T) {
"--user", address.Hex(), "--user", address.Hex(),
} }
log.Info(fmt.Sprintf("getting feed info with 'swarm feed info'")) log.Info("getting feed info with 'swarm feed info'")
cmd = runSwarm(t, flags...) cmd = runSwarm(t, flags...)
_, matches := cmd.ExpectRegexp(`.*`) // regex hack to extract stdout _, matches := cmd.ExpectRegexp(`.*`) // regex hack to extract stdout
cmd.ExpectExit() cmd.ExpectExit()
@ -141,9 +140,9 @@ func TestCLIFeedUpdate(t *testing.T) {
"--topic", topic.Hex(), "--topic", topic.Hex(),
} }
log.Info(fmt.Sprintf("Publishing manifest with 'swarm feed create'")) log.Info("Publishing manifest with 'swarm feed create'")
cmd = runSwarm(t, flags...) cmd = runSwarm(t, flags...)
_, matches = cmd.ExpectRegexp(`[a-f\d]{64}`) // regex hack to extract stdout _, matches = cmd.ExpectRegexp(`[a-f\d]{64}`)
cmd.ExpectExit() cmd.ExpectExit()
manifestAddress := matches[0] // read the received feed manifest manifestAddress := matches[0] // read the received feed manifest
@ -162,4 +161,36 @@ func TestCLIFeedUpdate(t *testing.T) {
if !bytes.Equal(data, retrieved) { if !bytes.Equal(data, retrieved) {
t.Fatalf("Received %s, expected %s", retrieved, data) t.Fatalf("Received %s, expected %s", retrieved, data)
} }
// test publishing a manifest for a different user
flags = []string{
"--bzzapi", srv.URL,
"feed", "create",
"--topic", topic.Hex(),
"--user", "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", // different user
}
log.Info("Publishing manifest with 'swarm feed create' for a different user")
cmd = runSwarm(t, flags...)
_, matches = cmd.ExpectRegexp(`[a-f\d]{64}`)
cmd.ExpectExit()
manifestAddress = matches[0] // read the received feed manifest
// now let's try to update that user's manifest which we don't have the private key for
flags = []string{
"--bzzapi", srv.URL,
"--bzzaccount", pkFileName,
"feed", "update",
"--manifest", manifestAddress,
hexData}
// create an update and expect an error given there is a user mismatch
log.Info("updating a feed with 'swarm feed update'")
cmd = runSwarm(t, flags...)
cmd.ExpectRegexp("Fatal:.*") // best way so far to detect a failure.
cmd.ExpectExit()
if cmd.ExitStatus() == 0 {
t.Fatal("Expected nonzero exit code when updating a manifest with the wrong user. Got 0.")
}
} }

View file

@ -156,6 +156,10 @@ var (
Name: "compressed", Name: "compressed",
Usage: "Prints encryption keys in compressed form", Usage: "Prints encryption keys in compressed form",
} }
SwarmBootnodeModeFlag = cli.BoolFlag{
Name: "bootnode-mode",
Usage: "Run Swarm in Bootnode mode",
}
SwarmFeedNameFlag = cli.StringFlag{ SwarmFeedNameFlag = cli.StringFlag{
Name: "name", Name: "name",
Usage: "User-defined name for the new feed, limited to 32 characters. If combined with topic, it will refer to a subtopic with this name", Usage: "User-defined name for the new feed, limited to 32 characters. If combined with topic, it will refer to a subtopic with this name",
@ -164,10 +168,6 @@ var (
Name: "topic", Name: "topic",
Usage: "User-defined topic this feed is tracking, hex encoded. Limited to 64 hexadecimal characters", Usage: "User-defined topic this feed is tracking, hex encoded. Limited to 64 hexadecimal characters",
} }
SwarmFeedDataOnCreateFlag = cli.StringFlag{
Name: "data",
Usage: "Initializes the feed with the given hex-encoded data. Data must be prefixed by 0x",
}
SwarmFeedManifestFlag = cli.StringFlag{ SwarmFeedManifestFlag = cli.StringFlag{
Name: "manifest", Name: "manifest",
Usage: "Refers to the feed through a manifest", Usage: "Refers to the feed through a manifest",

View file

@ -24,7 +24,7 @@ import (
"time" "time"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm/fuse" "github.com/ethereum/go-ethereum/swarm/fuse"
"gopkg.in/urfave/cli.v1" "gopkg.in/urfave/cli.v1"
@ -41,27 +41,24 @@ var fsCommand = cli.Command{
Action: mount, Action: mount,
CustomHelpTemplate: helpTemplate, CustomHelpTemplate: helpTemplate,
Name: "mount", Name: "mount",
Flags: []cli.Flag{utils.IPCPathFlag},
Usage: "mount a swarm hash to a mount point", Usage: "mount a swarm hash to a mount point",
ArgsUsage: "swarm fs mount --ipcpath <path to bzzd.ipc> <manifest hash> <mount point>", ArgsUsage: "swarm fs mount <manifest hash> <mount point>",
Description: "Mounts a Swarm manifest hash to a given mount point. This assumes you already have a Swarm node running locally. You must reference the correct path to your bzzd.ipc file", Description: "Mounts a Swarm manifest hash to a given mount point. This assumes you already have a Swarm node running locally. You must reference the correct path to your bzzd.ipc file",
}, },
{ {
Action: unmount, Action: unmount,
CustomHelpTemplate: helpTemplate, CustomHelpTemplate: helpTemplate,
Name: "unmount", Name: "unmount",
Flags: []cli.Flag{utils.IPCPathFlag},
Usage: "unmount a swarmfs mount", Usage: "unmount a swarmfs mount",
ArgsUsage: "swarm fs unmount --ipcpath <path to bzzd.ipc> <mount point>", ArgsUsage: "swarm fs unmount <mount point>",
Description: "Unmounts a swarmfs mount residing at <mount point>. This assumes you already have a Swarm node running locally. You must reference the correct path to your bzzd.ipc file", Description: "Unmounts a swarmfs mount residing at <mount point>. This assumes you already have a Swarm node running locally. You must reference the correct path to your bzzd.ipc file",
}, },
{ {
Action: listMounts, Action: listMounts,
CustomHelpTemplate: helpTemplate, CustomHelpTemplate: helpTemplate,
Name: "list", Name: "list",
Flags: []cli.Flag{utils.IPCPathFlag},
Usage: "list swarmfs mounts", Usage: "list swarmfs mounts",
ArgsUsage: "swarm fs list --ipcpath <path to bzzd.ipc>", ArgsUsage: "swarm fs list",
Description: "Lists all mounted swarmfs volumes. This assumes you already have a Swarm node running locally. You must reference the correct path to your bzzd.ipc file", Description: "Lists all mounted swarmfs volumes. This assumes you already have a Swarm node running locally. You must reference the correct path to your bzzd.ipc file",
}, },
}, },
@ -70,7 +67,7 @@ var fsCommand = cli.Command{
func mount(cliContext *cli.Context) { func mount(cliContext *cli.Context) {
args := cliContext.Args() args := cliContext.Args()
if len(args) < 2 { if len(args) < 2 {
utils.Fatalf("Usage: swarm fs mount --ipcpath <path to bzzd.ipc> <manifestHash> <file name>") utils.Fatalf("Usage: swarm fs mount <manifestHash> <file name>")
} }
client, err := dialRPC(cliContext) client, err := dialRPC(cliContext)
@ -97,7 +94,7 @@ func unmount(cliContext *cli.Context) {
args := cliContext.Args() args := cliContext.Args()
if len(args) < 1 { if len(args) < 1 {
utils.Fatalf("Usage: swarm fs unmount --ipcpath <path to bzzd.ipc> <mount path>") utils.Fatalf("Usage: swarm fs unmount <mount path>")
} }
client, err := dialRPC(cliContext) client, err := dialRPC(cliContext)
if err != nil { if err != nil {
@ -145,20 +142,21 @@ func listMounts(cliContext *cli.Context) {
} }
func dialRPC(ctx *cli.Context) (*rpc.Client, error) { func dialRPC(ctx *cli.Context) (*rpc.Client, error) {
var endpoint string endpoint := getIPCEndpoint(ctx)
log.Info("IPC endpoint", "path", endpoint)
if ctx.IsSet(utils.IPCPathFlag.Name) { return rpc.Dial(endpoint)
endpoint = ctx.String(utils.IPCPathFlag.Name)
} else {
utils.Fatalf("swarm ipc endpoint not specified")
} }
if endpoint == "" { func getIPCEndpoint(ctx *cli.Context) string {
endpoint = node.DefaultIPCEndpoint(clientIdentifier) cfg := defaultNodeConfig
} else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") { utils.SetNodeConfig(ctx, &cfg)
endpoint := cfg.IPCEndpoint()
if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") {
// Backwards compatibility with geth < 1.5 which required // Backwards compatibility with geth < 1.5 which required
// these prefixes. // these prefixes.
endpoint = endpoint[4:] endpoint = endpoint[4:]
} }
return rpc.Dial(endpoint) return endpoint
} }

View file

@ -20,6 +20,7 @@ package main
import ( import (
"bytes" "bytes"
"fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@ -28,6 +29,7 @@ import (
"testing" "testing"
"time" "time"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
) )
@ -36,6 +38,26 @@ type testFile struct {
content string content string
} }
// TestCLISwarmFsDefaultIPCPath tests if the most basic fs command, i.e., list
// can find and correctly connect to a running Swarm node on the default
// IPCPath.
func TestCLISwarmFsDefaultIPCPath(t *testing.T) {
cluster := newTestCluster(t, 1)
defer cluster.Shutdown()
handlingNode := cluster.Nodes[0]
list := runSwarm(t, []string{
"--datadir", handlingNode.Dir,
"fs",
"list",
}...)
list.WaitExit()
if list.Err != nil {
t.Fatal(list.Err)
}
}
// TestCLISwarmFs is a high-level test of swarmfs // TestCLISwarmFs is a high-level test of swarmfs
// //
// This test fails on travis for macOS as this executable exits with code 1 // This test fails on travis for macOS as this executable exits with code 1
@ -59,9 +81,9 @@ func TestCLISwarmFs(t *testing.T) {
log.Debug("swarmfs cli test: mounting first run", "ipc path", filepath.Join(handlingNode.Dir, handlingNode.IpcPath)) log.Debug("swarmfs cli test: mounting first run", "ipc path", filepath.Join(handlingNode.Dir, handlingNode.IpcPath))
mount := runSwarm(t, []string{ mount := runSwarm(t, []string{
fmt.Sprintf("--%s", utils.IPCPathFlag.Name), filepath.Join(handlingNode.Dir, handlingNode.IpcPath),
"fs", "fs",
"mount", "mount",
"--ipcpath", filepath.Join(handlingNode.Dir, handlingNode.IpcPath),
mhash, mhash,
mountPoint, mountPoint,
}...) }...)
@ -101,9 +123,9 @@ func TestCLISwarmFs(t *testing.T) {
log.Debug("swarmfs cli test: unmounting first run...", "ipc path", filepath.Join(handlingNode.Dir, handlingNode.IpcPath)) log.Debug("swarmfs cli test: unmounting first run...", "ipc path", filepath.Join(handlingNode.Dir, handlingNode.IpcPath))
unmount := runSwarm(t, []string{ unmount := runSwarm(t, []string{
fmt.Sprintf("--%s", utils.IPCPathFlag.Name), filepath.Join(handlingNode.Dir, handlingNode.IpcPath),
"fs", "fs",
"unmount", "unmount",
"--ipcpath", filepath.Join(handlingNode.Dir, handlingNode.IpcPath),
mountPoint, mountPoint,
}...) }...)
_, matches := unmount.ExpectRegexp(hashRegexp) _, matches := unmount.ExpectRegexp(hashRegexp)
@ -136,9 +158,9 @@ func TestCLISwarmFs(t *testing.T) {
//remount, check files //remount, check files
newMount := runSwarm(t, []string{ newMount := runSwarm(t, []string{
fmt.Sprintf("--%s", utils.IPCPathFlag.Name), filepath.Join(handlingNode.Dir, handlingNode.IpcPath),
"fs", "fs",
"mount", "mount",
"--ipcpath", filepath.Join(handlingNode.Dir, handlingNode.IpcPath),
hash, // the latest hash hash, // the latest hash
secondMountPoint, secondMountPoint,
}...) }...)
@ -172,9 +194,9 @@ func TestCLISwarmFs(t *testing.T) {
log.Debug("swarmfs cli test: unmounting second run", "ipc path", filepath.Join(handlingNode.Dir, handlingNode.IpcPath)) log.Debug("swarmfs cli test: unmounting second run", "ipc path", filepath.Join(handlingNode.Dir, handlingNode.IpcPath))
unmountSec := runSwarm(t, []string{ unmountSec := runSwarm(t, []string{
fmt.Sprintf("--%s", utils.IPCPathFlag.Name), filepath.Join(handlingNode.Dir, handlingNode.IpcPath),
"fs", "fs",
"unmount", "unmount",
"--ipcpath", filepath.Join(handlingNode.Dir, handlingNode.IpcPath),
secondMountPoint, secondMountPoint,
}...) }...)

View file

@ -66,9 +66,10 @@ OPTIONS:
{{end}}{{end}} {{end}}{{end}}
` `
var ( // Git SHA1 commit hash of the release (set via linker flags)
gitCommit string // Git SHA1 commit hash of the release (set via linker flags) // this variable will be assigned if corresponding parameter is passed with install, but not with test
) // e.g.: go install -ldflags "-X main.gitCommit=ed1312d01b19e04ef578946226e5d8069d5dfd5a" ./cmd/swarm
var gitCommit string
//declare a few constant error messages, useful for later error check comparisons in test //declare a few constant error messages, useful for later error check comparisons in test
var ( var (
@ -89,6 +90,7 @@ var defaultNodeConfig = node.DefaultConfig
// This init function sets defaults so cmd/swarm can run alongside geth. // This init function sets defaults so cmd/swarm can run alongside geth.
func init() { func init() {
sv.GitCommit = gitCommit
defaultNodeConfig.Name = clientIdentifier defaultNodeConfig.Name = clientIdentifier
defaultNodeConfig.Version = sv.VersionWithCommit(gitCommit) defaultNodeConfig.Version = sv.VersionWithCommit(gitCommit)
defaultNodeConfig.P2P.ListenAddr = ":30399" defaultNodeConfig.P2P.ListenAddr = ":30399"
@ -154,7 +156,6 @@ func init() {
utils.BootnodesFlag, utils.BootnodesFlag,
utils.KeyStoreDirFlag, utils.KeyStoreDirFlag,
utils.ListenPortFlag, utils.ListenPortFlag,
utils.NoDiscoverFlag,
utils.DiscoveryV5Flag, utils.DiscoveryV5Flag,
utils.NetrestrictFlag, utils.NetrestrictFlag,
utils.NodeKeyFileFlag, utils.NodeKeyFileFlag,
@ -187,6 +188,8 @@ func init() {
SwarmUploadDefaultPath, SwarmUploadDefaultPath,
SwarmUpFromStdinFlag, SwarmUpFromStdinFlag,
SwarmUploadMimeType, SwarmUploadMimeType,
// bootnode mode
SwarmBootnodeModeFlag,
// storage flags // storage flags
SwarmStorePath, SwarmStorePath,
SwarmStoreCapacity, SwarmStoreCapacity,
@ -227,12 +230,17 @@ func main() {
func keys(ctx *cli.Context) error { func keys(ctx *cli.Context) error {
privateKey := getPrivKey(ctx) privateKey := getPrivKey(ctx)
pub := hex.EncodeToString(crypto.FromECDSAPub(&privateKey.PublicKey)) pubkey := crypto.FromECDSAPub(&privateKey.PublicKey)
pubkeyhex := hex.EncodeToString(pubkey)
pubCompressed := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey)) pubCompressed := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey))
bzzkey := crypto.Keccak256Hash(pubkey).Hex()
if !ctx.Bool(SwarmCompressedFlag.Name) { if !ctx.Bool(SwarmCompressedFlag.Name) {
fmt.Println(fmt.Sprintf("publicKey=%s", pub)) fmt.Println(fmt.Sprintf("bzzkey=%s", bzzkey[2:]))
fmt.Println(fmt.Sprintf("publicKey=%s", pubkeyhex))
} }
fmt.Println(fmt.Sprintf("publicKeyCompressed=%s", pubCompressed)) fmt.Println(fmt.Sprintf("publicKeyCompressed=%s", pubCompressed))
return nil return nil
} }
@ -272,6 +280,10 @@ func bzzd(ctx *cli.Context) error {
setSwarmBootstrapNodes(ctx, &cfg) setSwarmBootstrapNodes(ctx, &cfg)
//setup the ethereum node //setup the ethereum node
utils.SetNodeConfig(ctx, &cfg) utils.SetNodeConfig(ctx, &cfg)
//always disable discovery from p2p package - swarm discovery is done with the `hive` protocol
cfg.P2P.NoDiscovery = true
stack, err := node.New(&cfg) stack, err := node.New(&cfg)
if err != nil { if err != nil {
utils.Fatalf("can't create node: %v", err) utils.Fatalf("can't create node: %v", err)
@ -294,6 +306,15 @@ func bzzd(ctx *cli.Context) error {
stack.Stop() stack.Stop()
}() }()
// add swarm bootnodes, because swarm doesn't use p2p package's discovery discv5
go func() {
s := stack.Server()
for _, n := range cfg.P2P.BootstrapNodes {
s.AddPeer(n)
}
}()
stack.Wait() stack.Wait()
return nil return nil
} }

View file

@ -254,7 +254,6 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
node.Cmd = runSwarm(t, node.Cmd = runSwarm(t,
"--port", p2pPort, "--port", p2pPort,
"--nat", "extip:127.0.0.1", "--nat", "extip:127.0.0.1",
"--nodiscover",
"--datadir", dir, "--datadir", dir,
"--ipcpath", conf.IPCPath, "--ipcpath", conf.IPCPath,
"--ens-api", "", "--ens-api", "",
@ -330,7 +329,6 @@ func newTestNode(t *testing.T, dir string) *testNode {
node.Cmd = runSwarm(t, node.Cmd = runSwarm(t,
"--port", p2pPort, "--port", p2pPort,
"--nat", "extip:127.0.0.1", "--nat", "extip:127.0.0.1",
"--nodiscover",
"--datadir", dir, "--datadir", dir,
"--ipcpath", conf.IPCPath, "--ipcpath", conf.IPCPath,
"--ens-api", "", "--ens-api", "",

View file

@ -2,11 +2,13 @@ package main
import ( import (
"bytes" "bytes"
"context"
"crypto/md5" "crypto/md5"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/httptrace"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
@ -16,9 +18,13 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/swarm/multihash" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/swarm/api/client"
"github.com/ethereum/go-ethereum/swarm/spancontext"
"github.com/ethereum/go-ethereum/swarm/storage/feed" "github.com/ethereum/go-ethereum/swarm/storage/feed"
"github.com/ethereum/go-ethereum/swarm/testutil"
colorable "github.com/mattn/go-colorable" colorable "github.com/mattn/go-colorable"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pborman/uuid" "github.com/pborman/uuid"
cli "gopkg.in/urfave/cli.v1" cli "gopkg.in/urfave/cli.v1"
) )
@ -27,16 +33,34 @@ const (
feedRandomDataLength = 8 feedRandomDataLength = 8
) )
// TODO: retrieve with manifest + extract repeating code
func cliFeedUploadAndSync(c *cli.Context) error { func cliFeedUploadAndSync(c *cli.Context) error {
metrics.GetOrRegisterCounter("feed-and-sync", nil).Inc(1)
log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))) log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))))
errc := make(chan error)
go func() {
errc <- feedUploadAndSync(c)
}()
select {
case err := <-errc:
if err != nil {
metrics.GetOrRegisterCounter("feed-and-sync.fail", nil).Inc(1)
}
return err
case <-time.After(time.Duration(timeout) * time.Second):
metrics.GetOrRegisterCounter("feed-and-sync.timeout", nil).Inc(1)
return fmt.Errorf("timeout after %v sec", timeout)
}
}
// TODO: retrieve with manifest + extract repeating code
func feedUploadAndSync(c *cli.Context) error {
defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size (kb)", filesize) }(time.Now()) defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size (kb)", filesize) }(time.Now())
generateEndpoints(scheme, cluster, from, to) generateEndpoints(scheme, cluster, appName, from, to)
log.Info("generating and uploading MRUs to " + endpoints[0] + " and syncing") log.Info("generating and uploading feeds to " + endpoints[0] + " and syncing")
// create a random private key to sign updates with and derive the address // create a random private key to sign updates with and derive the address
pkFile, err := ioutil.TempFile("", "swarm-feed-smoke-test") pkFile, err := ioutil.TempFile("", "swarm-feed-smoke-test")
@ -205,12 +229,12 @@ func cliFeedUploadAndSync(c *cli.Context) error {
log.Info("all endpoints synced random data successfully") log.Info("all endpoints synced random data successfully")
// upload test file // upload test file
log.Info("uploading to " + endpoints[0] + " and syncing") seed := int(time.Now().UnixNano() / 1e6)
log.Info("feed uploading to "+endpoints[0]+" and syncing", "seed", seed)
f, cleanup := generateRandomFile(filesize * 1000) randomBytes := testutil.RandomBytes(seed, filesize*1000)
defer cleanup()
hash, err := upload(f, endpoints[0]) hash, err := upload(&randomBytes, endpoints[0])
if err != nil { if err != nil {
return err return err
} }
@ -218,9 +242,8 @@ func cliFeedUploadAndSync(c *cli.Context) error {
if err != nil { if err != nil {
return err return err
} }
multihashHex := hexutil.Encode(multihash.ToMultihash(hashBytes)) multihashHex := hexutil.Encode(hashBytes)
fileHash, err := digest(bytes.NewReader(randomBytes))
fileHash, err := digest(f)
if err != nil { if err != nil {
return err return err
} }
@ -286,14 +309,37 @@ func cliFeedUploadAndSync(c *cli.Context) error {
} }
func fetchFeed(topic string, user string, endpoint string, original []byte, ruid string) error { func fetchFeed(topic string, user string, endpoint string, original []byte, ruid string) error {
ctx, sp := spancontext.StartSpan(context.Background(), "feed-and-sync.fetch")
defer sp.Finish()
log.Trace("sleeping", "ruid", ruid) log.Trace("sleeping", "ruid", ruid)
time.Sleep(3 * time.Second) time.Sleep(3 * time.Second)
log.Trace("http get request (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user) log.Trace("http get request (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user)
res, err := http.Get(endpoint + "/bzz-feed:/?topic=" + topic + "&user=" + user)
var tn time.Time
reqUri := endpoint + "/bzz-feed:/?topic=" + topic + "&user=" + user
req, _ := http.NewRequest("GET", reqUri, nil)
opentracing.GlobalTracer().Inject(
sp.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
trace := client.GetClientTrace("feed-and-sync - http get", "feed-and-sync", ruid, &tn)
req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
transport := http.DefaultTransport
//transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
tn = time.Now()
res, err := transport.RoundTrip(req)
if err != nil { if err != nil {
log.Error(err.Error(), "ruid", ruid)
return err return err
} }
log.Trace("http get response (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user, "code", res.StatusCode, "len", res.ContentLength) log.Trace("http get response (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user, "code", res.StatusCode, "len", res.ContentLength)
if res.StatusCode != 200 { if res.StatusCode != 200 {

View file

@ -17,23 +17,38 @@
package main package main
import ( import (
"fmt"
"os" "os"
"sort" "sort"
"github.com/ethereum/go-ethereum/cmd/utils"
gethmetrics "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/metrics/influxdb"
swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
"github.com/ethereum/go-ethereum/swarm/tracing"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
cli "gopkg.in/urfave/cli.v1" cli "gopkg.in/urfave/cli.v1"
) )
var (
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
)
var ( var (
endpoints []string endpoints []string
includeLocalhost bool includeLocalhost bool
cluster string cluster string
appName string
scheme string scheme string
filesize int filesize int
syncDelay int
from int from int
to int to int
verbosity int verbosity int
timeout int
single bool
) )
func main() { func main() {
@ -49,6 +64,12 @@ func main() {
Usage: "cluster to point to (prod or a given namespace)", Usage: "cluster to point to (prod or a given namespace)",
Destination: &cluster, Destination: &cluster,
}, },
cli.StringFlag{
Name: "app",
Value: "swarm",
Usage: "application to point to (swarm or swarm-private)",
Destination: &appName,
},
cli.IntFlag{ cli.IntFlag{
Name: "cluster-from", Name: "cluster-from",
Value: 8501, Value: 8501,
@ -78,14 +99,42 @@ func main() {
Usage: "file size for generated random file in KB", Usage: "file size for generated random file in KB",
Destination: &filesize, Destination: &filesize,
}, },
cli.IntFlag{
Name: "sync-delay",
Value: 5,
Usage: "duration of delay in seconds to wait for content to be synced",
Destination: &syncDelay,
},
cli.IntFlag{ cli.IntFlag{
Name: "verbosity", Name: "verbosity",
Value: 1, Value: 1,
Usage: "verbosity", Usage: "verbosity",
Destination: &verbosity, Destination: &verbosity,
}, },
cli.IntFlag{
Name: "timeout",
Value: 120,
Usage: "timeout in seconds after which kill the process",
Destination: &timeout,
},
cli.BoolFlag{
Name: "single",
Usage: "whether to fetch content from a single node or from all nodes",
Destination: &single,
},
} }
app.Flags = append(app.Flags, []cli.Flag{
utils.MetricsEnabledFlag,
swarmmetrics.MetricsInfluxDBEndpointFlag,
swarmmetrics.MetricsInfluxDBDatabaseFlag,
swarmmetrics.MetricsInfluxDBUsernameFlag,
swarmmetrics.MetricsInfluxDBPasswordFlag,
swarmmetrics.MetricsInfluxDBHostTagFlag,
}...)
app.Flags = append(app.Flags, tracing.Flags...)
app.Commands = []cli.Command{ app.Commands = []cli.Command{
{ {
Name: "upload_and_sync", Name: "upload_and_sync",
@ -99,13 +148,49 @@ func main() {
Usage: "feed update generate, upload and sync", Usage: "feed update generate, upload and sync",
Action: cliFeedUploadAndSync, Action: cliFeedUploadAndSync,
}, },
{
Name: "upload_speed",
Aliases: []string{"u"},
Usage: "measure upload speed",
Action: cliUploadSpeed,
},
} }
sort.Sort(cli.FlagsByName(app.Flags)) sort.Sort(cli.FlagsByName(app.Flags))
sort.Sort(cli.CommandsByName(app.Commands)) sort.Sort(cli.CommandsByName(app.Commands))
app.Before = func(ctx *cli.Context) error {
tracing.Setup(ctx)
return nil
}
app.After = func(ctx *cli.Context) error {
return emitMetrics(ctx)
}
err := app.Run(os.Args) err := app.Run(os.Args)
if err != nil { if err != nil {
log.Error(err.Error()) log.Error(err.Error())
os.Exit(1)
} }
} }
func emitMetrics(ctx *cli.Context) error {
if gethmetrics.Enabled {
var (
endpoint = ctx.GlobalString(swarmmetrics.MetricsInfluxDBEndpointFlag.Name)
database = ctx.GlobalString(swarmmetrics.MetricsInfluxDBDatabaseFlag.Name)
username = ctx.GlobalString(swarmmetrics.MetricsInfluxDBUsernameFlag.Name)
password = ctx.GlobalString(swarmmetrics.MetricsInfluxDBPasswordFlag.Name)
hosttag = ctx.GlobalString(swarmmetrics.MetricsInfluxDBHostTagFlag.Name)
)
return influxdb.InfluxDBWithTagsOnce(gethmetrics.DefaultRegistry, endpoint, database, username, password, "swarm-smoke.", map[string]string{
"host": hosttag,
"version": gitCommit,
"filesize": fmt.Sprintf("%v", filesize),
})
}
return nil
}

View file

@ -18,35 +18,44 @@ package main
import ( import (
"bytes" "bytes"
"context"
"crypto/md5" "crypto/md5"
crand "crypto/rand" crand "crypto/rand"
"crypto/tls"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
"math/rand"
"net/http" "net/http"
"net/http/httptrace"
"os" "os"
"os/exec"
"strings"
"sync" "sync"
"time" "time"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
colorable "github.com/mattn/go-colorable" "github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/swarm/api"
"github.com/ethereum/go-ethereum/swarm/api/client"
"github.com/ethereum/go-ethereum/swarm/spancontext"
"github.com/ethereum/go-ethereum/swarm/testutil"
opentracing "github.com/opentracing/opentracing-go"
"github.com/pborman/uuid" "github.com/pborman/uuid"
cli "gopkg.in/urfave/cli.v1" cli "gopkg.in/urfave/cli.v1"
) )
func generateEndpoints(scheme string, cluster string, from int, to int) { func generateEndpoints(scheme string, cluster string, app string, from int, to int) {
if cluster == "prod" { if cluster == "prod" {
for port := from; port <= to; port++ { for port := from; port < to; port++ {
endpoints = append(endpoints, fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, port)) endpoints = append(endpoints, fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, port))
} }
} else if cluster == "private-internal" {
for port := from; port < to; port++ {
endpoints = append(endpoints, fmt.Sprintf("%s://swarm-private-internal-%v:8500", scheme, port))
}
} else { } else {
for port := from; port <= to; port++ { for port := from; port < to; port++ {
endpoints = append(endpoints, fmt.Sprintf("%s://swarm-%v-%s.stg.swarm-gateways.net", scheme, port, cluster)) endpoints = append(endpoints, fmt.Sprintf("%s://%s-%v-%s.stg.swarm-gateways.net", scheme, app, port, cluster))
} }
} }
@ -57,24 +66,49 @@ func generateEndpoints(scheme string, cluster string, from int, to int) {
func cliUploadAndSync(c *cli.Context) error { func cliUploadAndSync(c *cli.Context) error {
log.PrintOrigins(true) log.PrintOrigins(true)
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))) log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size (kb)", filesize) }(time.Now()) metrics.GetOrRegisterCounter("upload-and-sync", nil).Inc(1)
generateEndpoints(scheme, cluster, from, to) errc := make(chan error)
go func() {
errc <- uploadAndSync(c)
}()
log.Info("uploading to " + endpoints[0] + " and syncing") select {
case err := <-errc:
if err != nil {
metrics.GetOrRegisterCounter("upload-and-sync.fail", nil).Inc(1)
}
return err
case <-time.After(time.Duration(timeout) * time.Second):
metrics.GetOrRegisterCounter("upload-and-sync.timeout", nil).Inc(1)
return fmt.Errorf("timeout after %v sec", timeout)
}
}
f, cleanup := generateRandomFile(filesize * 1000) func uploadAndSync(c *cli.Context) error {
defer cleanup() defer func(now time.Time) {
totalTime := time.Since(now)
log.Info("total time", "time", totalTime, "kb", filesize)
metrics.GetOrRegisterResettingTimer("upload-and-sync.total-time", nil).Update(totalTime)
}(time.Now())
hash, err := upload(f, endpoints[0]) generateEndpoints(scheme, cluster, appName, from, to)
seed := int(time.Now().UnixNano() / 1e6)
log.Info("uploading to "+endpoints[0]+" and syncing", "seed", seed)
randomBytes := testutil.RandomBytes(seed, filesize*1000)
t1 := time.Now()
hash, err := upload(&randomBytes, endpoints[0])
if err != nil { if err != nil {
log.Error(err.Error()) log.Error(err.Error())
return err return err
} }
metrics.GetOrRegisterResettingTimer("upload-and-sync.upload-time", nil).UpdateSince(t1)
fhash, err := digest(f) fhash, err := digest(bytes.NewReader(randomBytes))
if err != nil { if err != nil {
log.Error(err.Error()) log.Error(err.Error())
return err return err
@ -82,24 +116,46 @@ func cliUploadAndSync(c *cli.Context) error {
log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash)) log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash))
time.Sleep(3 * time.Second) time.Sleep(time.Duration(syncDelay) * time.Second)
wg := sync.WaitGroup{} wg := sync.WaitGroup{}
for _, endpoint := range endpoints { if single {
rand.Seed(time.Now().UTC().UnixNano())
randIndex := 1 + rand.Intn(len(endpoints)-1)
ruid := uuid.New()[:8] ruid := uuid.New()[:8]
wg.Add(1) wg.Add(1)
go func(endpoint string, ruid string) { go func(endpoint string, ruid string) {
for { for {
start := time.Now()
err := fetch(hash, endpoint, fhash, ruid) err := fetch(hash, endpoint, fhash, ruid)
if err != nil { if err != nil {
continue continue
} }
metrics.GetOrRegisterResettingTimer("upload-and-sync.single.fetch-time", nil).UpdateSince(start)
wg.Done()
return
}
}(endpoints[randIndex], ruid)
} else {
for _, endpoint := range endpoints[1:] {
ruid := uuid.New()[:8]
wg.Add(1)
go func(endpoint string, ruid string) {
for {
start := time.Now()
err := fetch(hash, endpoint, fhash, ruid)
if err != nil {
continue
}
metrics.GetOrRegisterResettingTimer("upload-and-sync.each.fetch-time", nil).UpdateSince(start)
wg.Done() wg.Done()
return return
} }
}(endpoint, ruid) }(endpoint, ruid)
} }
}
wg.Wait() wg.Wait()
log.Info("all endpoints synced random file successfully") log.Info("all endpoints synced random file successfully")
@ -108,16 +164,31 @@ func cliUploadAndSync(c *cli.Context) error {
// fetch is getting the requested `hash` from the `endpoint` and compares it with the `original` file // fetch is getting the requested `hash` from the `endpoint` and compares it with the `original` file
func fetch(hash string, endpoint string, original []byte, ruid string) error { func fetch(hash string, endpoint string, original []byte, ruid string) error {
log.Trace("sleeping", "ruid", ruid) ctx, sp := spancontext.StartSpan(context.Background(), "upload-and-sync.fetch")
time.Sleep(3 * time.Second) defer sp.Finish()
log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash) log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash)
client := &http.Client{Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, var tn time.Time
}} reqUri := endpoint + "/bzz:/" + hash + "/"
res, err := client.Get(endpoint + "/bzz:/" + hash + "/") req, _ := http.NewRequest("GET", reqUri, nil)
opentracing.GlobalTracer().Inject(
sp.Context(),
opentracing.HTTPHeaders,
opentracing.HTTPHeadersCarrier(req.Header))
trace := client.GetClientTrace("upload-and-sync - http get", "upload-and-sync", ruid, &tn)
req = req.WithContext(httptrace.WithClientTrace(ctx, trace))
transport := http.DefaultTransport
//transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
tn = time.Now()
res, err := transport.RoundTrip(req)
if err != nil { if err != nil {
log.Warn(err.Error(), "ruid", ruid) log.Error(err.Error(), "ruid", ruid)
return err return err
} }
log.Trace("http get response", "ruid", ruid, "api", endpoint, "hash", hash, "code", res.StatusCode, "len", res.ContentLength) log.Trace("http get response", "ruid", ruid, "api", endpoint, "hash", hash, "code", res.StatusCode, "len", res.ContentLength)
@ -148,16 +219,19 @@ func fetch(hash string, endpoint string, original []byte, ruid string) error {
} }
// upload is uploading a file `f` to `endpoint` via the `swarm up` cmd // upload is uploading a file `f` to `endpoint` via the `swarm up` cmd
func upload(f *os.File, endpoint string) (string, error) { func upload(dataBytes *[]byte, endpoint string) (string, error) {
var out bytes.Buffer swarm := client.NewClient(endpoint)
cmd := exec.Command("swarm", "--bzzapi", endpoint, "up", f.Name()) f := &client.File{
cmd.Stdout = &out ReadCloser: ioutil.NopCloser(bytes.NewReader(*dataBytes)),
err := cmd.Run() ManifestEntry: api.ManifestEntry{
if err != nil { ContentType: "text/plain",
return "", err Mode: 0660,
Size: int64(len(*dataBytes)),
},
} }
hash := strings.TrimRight(out.String(), "\r\n")
return hash, nil // upload data to bzz:// and retrieve the content-addressed manifest hash, hex-encoded.
return swarm.Upload(f, "", false)
} }
func digest(r io.Reader) ([]byte, error) { func digest(r io.Reader) ([]byte, error) {
@ -180,27 +254,3 @@ func generateRandomData(datasize int) ([]byte, error) {
} }
return b, nil return b, nil
} }
// generateRandomFile is creating a temporary file with the requested byte size
func generateRandomFile(size int) (f *os.File, teardown func()) {
// create a tmp file
tmp, err := ioutil.TempFile("", "swarm-test")
if err != nil {
panic(err)
}
// callback for tmp file cleanup
teardown = func() {
tmp.Close()
os.Remove(tmp.Name())
}
buf := make([]byte, size)
_, err = crand.Read(buf)
if err != nil {
panic(err)
}
ioutil.WriteFile(tmp.Name(), buf, 0755)
return tmp, teardown
}

View file

@ -0,0 +1,98 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"bytes"
"fmt"
"os"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/swarm/testutil"
cli "gopkg.in/urfave/cli.v1"
)
var endpoint string
//just use the first endpoint
func generateEndpoint(scheme string, cluster string, app string, from int) {
if cluster == "prod" {
endpoint = fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, from)
} else if cluster == "private-internal" {
endpoint = fmt.Sprintf("%s://swarm-private-internal-%v:8500", scheme, from)
} else {
endpoint = fmt.Sprintf("%s://%s-%v-%s.stg.swarm-gateways.net", scheme, app, from, cluster)
}
}
func cliUploadSpeed(c *cli.Context) error {
log.PrintOrigins(true)
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
metrics.GetOrRegisterCounter("upload-speed", nil).Inc(1)
errc := make(chan error)
go func() {
errc <- uploadSpeed(c)
}()
select {
case err := <-errc:
if err != nil {
metrics.GetOrRegisterCounter("upload-speed.fail", nil).Inc(1)
}
return err
case <-time.After(time.Duration(timeout) * time.Second):
metrics.GetOrRegisterCounter("upload-speed.timeout", nil).Inc(1)
return fmt.Errorf("timeout after %v sec", timeout)
}
}
func uploadSpeed(c *cli.Context) error {
defer func(now time.Time) {
totalTime := time.Since(now)
log.Info("total time", "time", totalTime, "kb", filesize)
metrics.GetOrRegisterCounter("upload-speed.total-time", nil).Inc(int64(totalTime))
}(time.Now())
generateEndpoint(scheme, cluster, appName, from)
seed := int(time.Now().UnixNano() / 1e6)
log.Info("uploading to "+endpoint, "seed", seed)
randomBytes := testutil.RandomBytes(seed, filesize*1000)
t1 := time.Now()
hash, err := upload(&randomBytes, endpoint)
if err != nil {
log.Error(err.Error())
return err
}
metrics.GetOrRegisterCounter("upload-speed.upload-time", nil).Inc(int64(time.Since(t1)))
fhash, err := digest(bytes.NewReader(randomBytes))
if err != nil {
log.Error(err.Error())
return err
}
log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash))
return nil
}

View file

@ -0,0 +1,157 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"context"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"path"
"path/filepath"
"strings"
"sync"
"time"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p/simulations"
"github.com/ethereum/go-ethereum/p2p/simulations/adapters"
"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/network/simulation"
cli "gopkg.in/urfave/cli.v1"
)
// create is used as the entry function for "create" app command.
func create(ctx *cli.Context) error {
log.PrintOrigins(true)
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(ctx.Int("verbosity")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
if len(ctx.Args()) < 1 {
return errors.New("argument should be the filename to verify or write-to")
}
filename, err := touchPath(ctx.Args()[0])
if err != nil {
return err
}
return createSnapshot(filename, ctx.Int("nodes"), strings.Split(ctx.String("services"), ","))
}
// createSnapshot creates a new snapshot on filesystem with provided filename,
// number of nodes and service names.
func createSnapshot(filename string, nodes int, services []string) (err error) {
log.Debug("create snapshot", "filename", filename, "nodes", nodes, "services", services)
sim := simulation.New(map[string]simulation.ServiceFunc{
"bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
addr := network.NewAddr(ctx.Config.Node())
kad := network.NewKademlia(addr.Over(), network.NewKadParams())
hp := network.NewHiveParams()
hp.KeepAliveInterval = time.Duration(200) * time.Millisecond
hp.Discovery = true // discovery must be enabled when creating a snapshot
config := &network.BzzConfig{
OverlayAddr: addr.Over(),
UnderlayAddr: addr.Under(),
HiveParams: hp,
}
return network.NewBzz(config, kad, nil, nil, nil), nil, nil
},
})
defer sim.Close()
_, err = sim.AddNodes(nodes)
if err != nil {
return fmt.Errorf("add nodes: %v", err)
}
err = sim.Net.ConnectNodesRing(nil)
if err != nil {
return fmt.Errorf("connect nodes: %v", err)
}
ctx, cancelSimRun := context.WithTimeout(context.Background(), 2*time.Minute)
defer cancelSimRun()
if _, err := sim.WaitTillHealthy(ctx); err != nil {
return fmt.Errorf("wait for healthy kademlia: %v", err)
}
var snap *simulations.Snapshot
if len(services) > 0 {
// If service names are provided, include them in the snapshot.
// But, check if "bzz" service is not among them to remove it
// form the snapshot as it exists on snapshot creation.
var removeServices []string
var wantBzz bool
for _, s := range services {
if s == "bzz" {
wantBzz = true
break
}
}
if !wantBzz {
removeServices = []string{"bzz"}
}
snap, err = sim.Net.SnapshotWithServices(services, removeServices)
} else {
snap, err = sim.Net.Snapshot()
}
if err != nil {
return fmt.Errorf("create snapshot: %v", err)
}
jsonsnapshot, err := json.Marshal(snap)
if err != nil {
return fmt.Errorf("json encode snapshot: %v", err)
}
return ioutil.WriteFile(filename, jsonsnapshot, 0666)
}
// touchPath creates an empty file and all subdirectories
// that are missing.
func touchPath(filename string) (string, error) {
if path.IsAbs(filename) {
if _, err := os.Stat(filename); err == nil {
// path exists, overwrite
return filename, nil
}
}
d, f := path.Split(filename)
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
return "", err
}
_, err = os.Stat(path.Join(dir, filename))
if err == nil {
// path exists, overwrite
return filename, nil
}
dirPath := path.Join(dir, d)
filePath := path.Join(dirPath, f)
if d != "" {
err = os.MkdirAll(dirPath, os.ModeDir)
if err != nil {
return "", err
}
}
return filePath, nil
}

View file

@ -0,0 +1,143 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"os"
"runtime"
"sort"
"strconv"
"strings"
"testing"
"github.com/ethereum/go-ethereum/p2p/simulations"
)
// TestSnapshotCreate is a high level e2e test that tests for snapshot generation.
// It runs a few "create" commands with different flag values and loads generated
// snapshot files to validate their content.
func TestSnapshotCreate(t *testing.T) {
if runtime.GOOS == "windows" {
t.Skip()
}
for _, v := range []struct {
name string
nodes int
services string
}{
{
name: "defaults",
},
{
name: "more nodes",
nodes: defaultNodes + 5,
},
{
name: "services",
services: "stream,pss,zorglub",
},
{
name: "services with bzz",
services: "bzz,pss",
},
} {
t.Run(v.name, func(t *testing.T) {
t.Parallel()
file, err := ioutil.TempFile("", "swarm-snapshot")
if err != nil {
t.Fatal(err)
}
defer os.Remove(file.Name())
if err = file.Close(); err != nil {
t.Error(err)
}
args := []string{"create"}
if v.nodes > 0 {
args = append(args, "--nodes", strconv.Itoa(v.nodes))
}
if v.services != "" {
args = append(args, "--services", v.services)
}
testCmd := runSnapshot(t, append(args, file.Name())...)
testCmd.ExpectExit()
if code := testCmd.ExitStatus(); code != 0 {
t.Fatalf("command exit code %v, expected 0", code)
}
f, err := os.Open(file.Name())
if err != nil {
t.Fatal(err)
}
defer func() {
err := f.Close()
if err != nil {
t.Error("closing snapshot file", "err", err)
}
}()
b, err := ioutil.ReadAll(f)
if err != nil {
t.Fatal(err)
}
var snap simulations.Snapshot
err = json.Unmarshal(b, &snap)
if err != nil {
t.Fatal(err)
}
wantNodes := v.nodes
if wantNodes == 0 {
wantNodes = defaultNodes
}
gotNodes := len(snap.Nodes)
if gotNodes != wantNodes {
t.Errorf("got %v nodes, want %v", gotNodes, wantNodes)
}
if len(snap.Conns) == 0 {
t.Error("no connections in a snapshot")
}
var wantServices []string
if v.services != "" {
wantServices = strings.Split(v.services, ",")
} else {
wantServices = []string{"bzz"}
}
// sort service names so they can be comparable
// as strings to every node sorted services
sort.Strings(wantServices)
for i, n := range snap.Nodes {
gotServices := n.Node.Config.Services
sort.Strings(gotServices)
if fmt.Sprint(gotServices) != fmt.Sprint(wantServices) {
t.Errorf("got services %v for node %v, want %v", gotServices, i, wantServices)
}
}
})
}
}

View file

@ -0,0 +1,82 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"os"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/log"
cli "gopkg.in/urfave/cli.v1"
)
var gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
// default value for "create" command --nodes flag
const defaultNodes = 10
func main() {
err := newApp().Run(os.Args)
if err != nil {
log.Error(err.Error())
os.Exit(1)
}
}
// newApp construct a new instance of Swarm Snapshot Utility.
// Method Run is called on it in the main function and in tests.
func newApp() (app *cli.App) {
app = utils.NewApp(gitCommit, "Swarm Snapshot Utility")
app.Name = "swarm-snapshot"
app.Usage = ""
// app flags (for all commands)
app.Flags = []cli.Flag{
cli.IntFlag{
Name: "verbosity",
Value: 1,
Usage: "verbosity level",
},
}
app.Commands = []cli.Command{
{
Name: "create",
Aliases: []string{"c"},
Usage: "create a swarm snapshot",
Action: create,
// Flags only for "create" command.
// Allow app flags to be specified after the
// command argument.
Flags: append(app.Flags,
cli.IntFlag{
Name: "nodes",
Value: defaultNodes,
Usage: "number of nodes",
},
cli.StringFlag{
Name: "services",
Value: "bzz",
Usage: "comma separated list of services to boot the nodes with",
},
),
},
}
return app
}

View file

@ -0,0 +1,49 @@
// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
import (
"fmt"
"os"
"testing"
"github.com/docker/docker/pkg/reexec"
"github.com/ethereum/go-ethereum/internal/cmdtest"
)
func init() {
reexec.Register("swarm-snapshot", func() {
if err := newApp().Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
os.Exit(0)
})
}
func runSnapshot(t *testing.T, args ...string) *cmdtest.TestCmd {
tt := cmdtest.NewTestCmd(t, nil)
tt.Run("swarm-snapshot", args...)
return tt
}
func TestMain(m *testing.M) {
if reexec.Init() {
return
}
os.Exit(m.Run())
}

View file

@ -19,6 +19,7 @@ package utils
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"encoding/json"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"math/big" "math/big"
@ -140,6 +141,10 @@ var (
Name: "rinkeby", Name: "rinkeby",
Usage: "Rinkeby network: pre-configured proof-of-authority test network", Usage: "Rinkeby network: pre-configured proof-of-authority test network",
} }
ConstantinopleOverrideFlag = cli.Uint64Flag{
Name: "override.constantinople",
Usage: "Manually specify constantinople fork-block, overriding the bundled setting",
}
DeveloperFlag = cli.BoolFlag{ DeveloperFlag = cli.BoolFlag{
Name: "dev", Name: "dev",
Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled", Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled",
@ -161,6 +166,22 @@ var (
Name: "exitwhensynced", Name: "exitwhensynced",
Usage: "Exists syncing after block synchronisation", Usage: "Exists syncing after block synchronisation",
} }
ULCModeConfigFlag = cli.StringFlag{
Name: "ulc.config",
Usage: "Config file to use for ultra light client mode",
}
OnlyAnnounceModeFlag = cli.BoolFlag{
Name: "ulc.onlyannounce",
Usage: "ULC server sends announcements only",
}
ULCMinTrustedFractionFlag = cli.IntFlag{
Name: "ulc.fraction",
Usage: "Minimum % of trusted ULC servers required to announce a new head",
}
ULCTrustedNodesFlag = cli.StringFlag{
Name: "ulc.trusted",
Usage: "List of trusted ULC servers",
}
defaultSyncMode = eth.DefaultConfig.SyncMode defaultSyncMode = eth.DefaultConfig.SyncMode
SyncModeFlag = TextMarshalerFlag{ SyncModeFlag = TextMarshalerFlag{
Name: "syncmode", Name: "syncmode",
@ -186,6 +207,10 @@ var (
Name: "lightkdf", Name: "lightkdf",
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength", Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
} }
WhitelistFlag = cli.StringFlag{
Name: "whitelist",
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
}
// Dashboard settings // Dashboard settings
DashboardEnabledFlag = cli.BoolFlag{ DashboardEnabledFlag = cli.BoolFlag{
Name: metrics.DashboardEnabledFlag, Name: metrics.DashboardEnabledFlag,
@ -431,6 +456,30 @@ var (
Usage: "HTTP-RPC server listening port", Usage: "HTTP-RPC server listening port",
Value: node.DefaultHTTPPort, Value: node.DefaultHTTPPort,
} }
GraphQLEnabledFlag = cli.BoolFlag{
Name: "graphql",
Usage: "Enable the GraphQL server",
}
GraphQLListenAddrFlag = cli.StringFlag{
Name: "graphql.addr",
Usage: "GraphQL server listening interface",
Value: node.DefaultGraphQLHost,
}
GraphQLPortFlag = cli.IntFlag{
Name: "graphql.port",
Usage: "GraphQL server listening port",
Value: node.DefaultGraphQLPort,
}
GraphQLCORSDomainFlag = cli.StringFlag{
Name: "graphql.rpccorsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: "",
}
GraphQLVirtualHostsFlag = cli.StringFlag{
Name: "graphql.rpcvhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
}
RPCCORSDomainFlag = cli.StringFlag{ RPCCORSDomainFlag = cli.StringFlag{
Name: "rpccorsdomain", Name: "rpccorsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)", Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
@ -703,6 +752,7 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
cfg.BootstrapNodes = make([]*enode.Node, 0, len(urls)) cfg.BootstrapNodes = make([]*enode.Node, 0, len(urls))
for _, url := range urls { for _, url := range urls {
if url != "" {
node, err := enode.ParseV4(url) node, err := enode.ParseV4(url)
if err != nil { if err != nil {
log.Crit("Bootstrap URL invalid", "enode", url, "err", err) log.Crit("Bootstrap URL invalid", "enode", url, "err", err)
@ -710,6 +760,7 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
cfg.BootstrapNodes = append(cfg.BootstrapNodes, node) cfg.BootstrapNodes = append(cfg.BootstrapNodes, node)
} }
} }
}
// setBootstrapNodesV5 creates a list of bootstrap nodes from the command line // setBootstrapNodesV5 creates a list of bootstrap nodes from the command line
// flags, reverting to pre-configured ones if none have been specified. // flags, reverting to pre-configured ones if none have been specified.
@ -792,6 +843,24 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
} }
} }
// setGraphQL creates the GraphQL listener interface string from the set
// command line flags, returning empty if the GraphQL endpoint is disabled.
func setGraphQL(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalBool(GraphQLEnabledFlag.Name) && cfg.GraphQLHost == "" {
cfg.GraphQLHost = "127.0.0.1"
if ctx.GlobalIsSet(GraphQLListenAddrFlag.Name) {
cfg.GraphQLHost = ctx.GlobalString(GraphQLListenAddrFlag.Name)
}
}
cfg.GraphQLPort = ctx.GlobalInt(GraphQLPortFlag.Name)
if ctx.GlobalIsSet(GraphQLCORSDomainFlag.Name) {
cfg.GraphQLCors = splitAndTrim(ctx.GlobalString(GraphQLCORSDomainFlag.Name))
}
if ctx.GlobalIsSet(GraphQLVirtualHostsFlag.Name) {
cfg.GraphQLVirtualHosts = splitAndTrim(ctx.GlobalString(GraphQLVirtualHostsFlag.Name))
}
}
// setWS creates the WebSocket RPC listener interface string from the set // setWS creates the WebSocket RPC listener interface string from the set
// command line flags, returning empty if the HTTP endpoint is disabled. // command line flags, returning empty if the HTTP endpoint is disabled.
func setWS(ctx *cli.Context, cfg *node.Config) { func setWS(ctx *cli.Context, cfg *node.Config) {
@ -825,21 +894,50 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
} }
} }
// SetULC setup ULC config from file if given.
func SetULC(ctx *cli.Context, cfg *eth.Config) {
// ULC config isn't loaded from global config and ULC config and ULC trusted nodes are not defined.
if cfg.ULC == nil && !(ctx.GlobalIsSet(ULCModeConfigFlag.Name) || ctx.GlobalIsSet(ULCTrustedNodesFlag.Name)) {
return
}
cfg.ULC = &eth.ULCConfig{}
path := ctx.GlobalString(ULCModeConfigFlag.Name)
if path != "" {
cfgData, err := ioutil.ReadFile(path)
if err != nil {
Fatalf("Failed to unmarshal ULC configuration: %v", err)
}
err = json.Unmarshal(cfgData, &cfg.ULC)
if err != nil {
Fatalf("Failed to unmarshal ULC configuration: %s", err.Error())
}
}
if trustedNodes := ctx.GlobalString(ULCTrustedNodesFlag.Name); trustedNodes != "" {
cfg.ULC.TrustedServers = strings.Split(trustedNodes, ",")
}
if trustedFraction := ctx.GlobalInt(ULCMinTrustedFractionFlag.Name); trustedFraction > 0 {
cfg.ULC.MinTrustedFraction = trustedFraction
}
if cfg.ULC.MinTrustedFraction <= 0 && cfg.ULC.MinTrustedFraction > 100 {
log.Error("MinTrustedFraction is invalid", "MinTrustedFraction", cfg.ULC.MinTrustedFraction, "Changed to default", eth.DefaultULCMinTrustedFraction)
cfg.ULC.MinTrustedFraction = eth.DefaultULCMinTrustedFraction
}
}
// makeDatabaseHandles raises out the number of allowed file handles per process // makeDatabaseHandles raises out the number of allowed file handles per process
// for Geth and returns half of the allowance to assign to the database. // for Geth and returns half of the allowance to assign to the database.
func makeDatabaseHandles() int { func makeDatabaseHandles() int {
limit, err := fdlimit.Current() limit, err := fdlimit.Maximum()
if err != nil { if err != nil {
Fatalf("Failed to retrieve file descriptor allowance: %v", err) Fatalf("Failed to retrieve file descriptor allowance: %v", err)
} }
if limit < 2048 { if err := fdlimit.Raise(uint64(limit)); err != nil {
if err := fdlimit.Raise(2048); err != nil {
Fatalf("Failed to raise file descriptor allowance: %v", err) Fatalf("Failed to raise file descriptor allowance: %v", err)
} }
}
if limit > 2048 { // cap database file descriptors even if more is available
limit = 2048
}
return limit / 2 // Leave half for networking and other stuff return limit / 2 // Leave half for networking and other stuff
} }
@ -979,19 +1077,11 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
SetP2PConfig(ctx, &cfg.P2P) SetP2PConfig(ctx, &cfg.P2P)
setIPC(ctx, cfg) setIPC(ctx, cfg)
setHTTP(ctx, cfg) setHTTP(ctx, cfg)
setGraphQL(ctx, cfg)
setWS(ctx, cfg) setWS(ctx, cfg)
setNodeUserIdent(ctx, cfg) setNodeUserIdent(ctx, cfg)
switch { setDataDir(ctx, cfg)
case ctx.GlobalIsSet(DataDirFlag.Name):
cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
case ctx.GlobalBool(DeveloperFlag.Name):
cfg.DataDir = "" // unless explicitly requested, use memory databases
case ctx.GlobalBool(TestnetFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
case ctx.GlobalBool(RinkebyFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
}
if ctx.GlobalIsSet(KeyStoreDirFlag.Name) { if ctx.GlobalIsSet(KeyStoreDirFlag.Name) {
cfg.KeyStoreDir = ctx.GlobalString(KeyStoreDirFlag.Name) cfg.KeyStoreDir = ctx.GlobalString(KeyStoreDirFlag.Name)
@ -1004,6 +1094,19 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
} }
} }
func setDataDir(ctx *cli.Context, cfg *node.Config) {
switch {
case ctx.GlobalIsSet(DataDirFlag.Name):
cfg.DataDir = ctx.GlobalString(DataDirFlag.Name)
case ctx.GlobalBool(DeveloperFlag.Name):
cfg.DataDir = "" // unless explicitly requested, use memory databases
case ctx.GlobalBool(TestnetFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
case ctx.GlobalBool(RinkebyFlag.Name):
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
}
}
func setGPO(ctx *cli.Context, cfg *gasprice.Config) { func setGPO(ctx *cli.Context, cfg *gasprice.Config) {
if ctx.GlobalIsSet(GpoBlocksFlag.Name) { if ctx.GlobalIsSet(GpoBlocksFlag.Name) {
cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name) cfg.Blocks = ctx.GlobalInt(GpoBlocksFlag.Name)
@ -1077,6 +1180,29 @@ func setEthash(ctx *cli.Context, cfg *eth.Config) {
} }
} }
func setWhitelist(ctx *cli.Context, cfg *eth.Config) {
whitelist := ctx.GlobalString(WhitelistFlag.Name)
if whitelist == "" {
return
}
cfg.Whitelist = make(map[uint64]common.Hash)
for _, entry := range strings.Split(whitelist, ",") {
parts := strings.Split(entry, "=")
if len(parts) != 2 {
Fatalf("Invalid whitelist entry: %s", entry)
}
number, err := strconv.ParseUint(parts[0], 0, 64)
if err != nil {
Fatalf("Invalid whitelist block number %s: %v", parts[0], err)
}
var hash common.Hash
if err = hash.UnmarshalText([]byte(parts[1])); err != nil {
Fatalf("Invalid whitelist hash %s: %v", parts[1], err)
}
cfg.Whitelist[number] = hash
}
}
// checkExclusive verifies that only a single instance of the provided flags was // checkExclusive verifies that only a single instance of the provided flags was
// set by the user. Each flag might optionally be followed by a string type to // set by the user. Each flag might optionally be followed by a string type to
// specialize it further. // specialize it further.
@ -1142,6 +1268,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
setGPO(ctx, &cfg.GPO) setGPO(ctx, &cfg.GPO)
setTxPool(ctx, &cfg.TxPool) setTxPool(ctx, &cfg.TxPool)
setEthash(ctx, cfg) setEthash(ctx, cfg)
setWhitelist(ctx, cfg)
if ctx.GlobalIsSet(SyncModeFlag.Name) { if ctx.GlobalIsSet(SyncModeFlag.Name) {
cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode) cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode)
@ -1152,6 +1279,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
if ctx.GlobalIsSet(LightPeersFlag.Name) { if ctx.GlobalIsSet(LightPeersFlag.Name) {
cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name) cfg.LightPeers = ctx.GlobalInt(LightPeersFlag.Name)
} }
if ctx.GlobalIsSet(OnlyAnnounceModeFlag.Name) {
cfg.OnlyAnnounce = ctx.GlobalBool(OnlyAnnounceModeFlag.Name)
}
if ctx.GlobalIsSet(NetworkIdFlag.Name) { if ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name) cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
} }
@ -1379,7 +1509,6 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) { func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) {
var err error var err error
chainDb = MakeChainDatabase(ctx, stack) chainDb = MakeChainDatabase(ctx, stack)
config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx)) config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx))
if err != nil { if err != nil {
Fatalf("%v", err) Fatalf("%v", err)

View file

@ -72,6 +72,25 @@ func (b Bytes) String() string {
return Encode(b) return Encode(b)
} }
// ImplementsGraphQLType returns true if Bytes implements the specified GraphQL type.
func (b Bytes) ImplementsGraphQLType(name string) bool { return name == "Bytes" }
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
func (b *Bytes) UnmarshalGraphQL(input interface{}) error {
var err error
switch input := input.(type) {
case string:
data, err := Decode(input)
if err != nil {
return err
}
*b = data
default:
err = fmt.Errorf("Unexpected type for Bytes: %v", input)
}
return err
}
// UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out // UnmarshalFixedJSON decodes the input as a string with 0x prefix. The length of out
// determines the required input length. This function is commonly used to implement the // determines the required input length. This function is commonly used to implement the
// UnmarshalJSON method for fixed-size types. // UnmarshalJSON method for fixed-size types.
@ -187,6 +206,25 @@ func (b *Big) String() string {
return EncodeBig(b.ToInt()) return EncodeBig(b.ToInt())
} }
// ImplementsGraphQLType returns true if Big implements the provided GraphQL type.
func (b Big) ImplementsGraphQLType(name string) bool { return name == "BigInt" }
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
func (b *Big) UnmarshalGraphQL(input interface{}) error {
var err error
switch input := input.(type) {
case string:
return b.UnmarshalText([]byte(input))
case int32:
var num big.Int
num.SetInt64(int64(input))
*b = Big(num)
default:
err = fmt.Errorf("Unexpected type for BigInt: %v", input)
}
return err
}
// Uint64 marshals/unmarshals as a JSON string with 0x prefix. // Uint64 marshals/unmarshals as a JSON string with 0x prefix.
// The zero value marshals as "0x0". // The zero value marshals as "0x0".
type Uint64 uint64 type Uint64 uint64
@ -234,6 +272,23 @@ func (b Uint64) String() string {
return EncodeUint64(uint64(b)) return EncodeUint64(uint64(b))
} }
// ImplementsGraphQLType returns true if Uint64 implements the provided GraphQL type.
func (b Uint64) ImplementsGraphQLType(name string) bool { return name == "Long" }
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
func (b *Uint64) UnmarshalGraphQL(input interface{}) error {
var err error
switch input := input.(type) {
case string:
return b.UnmarshalText([]byte(input))
case int32:
*b = Uint64(input)
default:
err = fmt.Errorf("Unexpected type for Long: %v", input)
}
return err
}
// Uint marshals/unmarshals as a JSON string with 0x prefix. // Uint marshals/unmarshals as a JSON string with 0x prefix.
// The zero value marshals as "0x0". // The zero value marshals as "0x0".
type Uint uint type Uint uint

View file

@ -27,7 +27,7 @@ import (
"strings" "strings"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto/sha3" "golang.org/x/crypto/sha3"
) )
// Lengths of hashes and addresses in bytes. // Lengths of hashes and addresses in bytes.
@ -141,6 +141,21 @@ func (h Hash) Value() (driver.Value, error) {
return h[:], nil return h[:], nil
} }
// ImplementsGraphQLType returns true if Hash implements the specified GraphQL type.
func (_ Hash) ImplementsGraphQLType(name string) bool { return name == "Bytes32" }
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
func (h *Hash) UnmarshalGraphQL(input interface{}) error {
var err error
switch input := input.(type) {
case string:
*h = HexToHash(input)
default:
err = fmt.Errorf("Unexpected type for Bytes32: %v", input)
}
return err
}
// UnprefixedHash allows marshaling a Hash without 0x prefix. // UnprefixedHash allows marshaling a Hash without 0x prefix.
type UnprefixedHash Hash type UnprefixedHash Hash
@ -196,7 +211,7 @@ func (a Address) Hash() Hash { return BytesToHash(a[:]) }
// Hex returns an EIP55-compliant hex string representation of the address. // Hex returns an EIP55-compliant hex string representation of the address.
func (a Address) Hex() string { func (a Address) Hex() string {
unchecksummed := hex.EncodeToString(a[:]) unchecksummed := hex.EncodeToString(a[:])
sha := sha3.NewKeccak256() sha := sha3.NewLegacyKeccak256()
sha.Write([]byte(unchecksummed)) sha.Write([]byte(unchecksummed))
hash := sha.Sum(nil) hash := sha.Sum(nil)
@ -268,6 +283,21 @@ func (a Address) Value() (driver.Value, error) {
return a[:], nil return a[:], nil
} }
// ImplementsGraphQLType returns true if Hash implements the specified GraphQL type.
func (a Address) ImplementsGraphQLType(name string) bool { return name == "Address" }
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
func (a *Address) UnmarshalGraphQL(input interface{}) error {
var err error
switch input := input.(type) {
case string:
*a = HexToAddress(input)
default:
err = fmt.Errorf("Unexpected type for Address: %v", input)
}
return err
}
// UnprefixedAddress allows marshaling an Address without 0x prefix. // UnprefixedAddress allows marshaling an Address without 0x prefix.
type UnprefixedAddress Address type UnprefixedAddress Address

View file

@ -33,13 +33,13 @@ import (
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
lru "github.com/hashicorp/golang-lru" lru "github.com/hashicorp/golang-lru"
"golang.org/x/crypto/sha3"
) )
const ( const (
@ -148,7 +148,7 @@ type SignerFn func(accounts.Account, []byte) ([]byte, error)
// panics. This is done to avoid accidentally using both forms (signature present // panics. This is done to avoid accidentally using both forms (signature present
// or not), which could be abused to produce different hashes for the same header. // or not), which could be abused to produce different hashes for the same header.
func sigHash(header *types.Header) (hash common.Hash) { func sigHash(header *types.Header) (hash common.Hash) {
hasher := sha3.NewKeccak256() hasher := sha3.NewLegacyKeccak256()
rlp.Encode(hasher, []interface{}{ rlp.Encode(hasher, []interface{}{
header.ParentHash, header.ParentHash,

View file

@ -30,8 +30,8 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/bitutil" "github.com/ethereum/go-ethereum/common/bitutil"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"golang.org/x/crypto/sha3"
) )
const ( const (
@ -123,7 +123,7 @@ func seedHash(block uint64) []byte {
if block < epochLength { if block < epochLength {
return seed return seed
} }
keccak256 := makeHasher(sha3.NewKeccak256()) keccak256 := makeHasher(sha3.NewLegacyKeccak256())
for i := 0; i < int(block/epochLength); i++ { for i := 0; i < int(block/epochLength); i++ {
keccak256(seed, seed) keccak256(seed, seed)
} }
@ -177,7 +177,7 @@ func generateCache(dest []uint32, epoch uint64, seed []byte) {
} }
}() }()
// Create a hasher to reuse between invocations // Create a hasher to reuse between invocations
keccak512 := makeHasher(sha3.NewKeccak512()) keccak512 := makeHasher(sha3.NewLegacyKeccak512())
// Sequentially produce the initial dataset // Sequentially produce the initial dataset
keccak512(cache, seed) keccak512(cache, seed)
@ -301,7 +301,7 @@ func generateDataset(dest []uint32, epoch uint64, cache []uint32) {
defer pend.Done() defer pend.Done()
// Create a hasher to reuse between invocations // Create a hasher to reuse between invocations
keccak512 := makeHasher(sha3.NewKeccak512()) keccak512 := makeHasher(sha3.NewLegacyKeccak512())
// Calculate the data segment this thread should generate // Calculate the data segment this thread should generate
batch := uint32((size + hashBytes*uint64(threads) - 1) / (hashBytes * uint64(threads))) batch := uint32((size + hashBytes*uint64(threads) - 1) / (hashBytes * uint64(threads)))
@ -375,7 +375,7 @@ func hashimoto(hash []byte, nonce uint64, size uint64, lookup func(index uint32)
// in-memory cache) in order to produce our final value for a particular header // in-memory cache) in order to produce our final value for a particular header
// hash and nonce. // hash and nonce.
func hashimotoLight(size uint64, cache []uint32, hash []byte, nonce uint64) ([]byte, []byte) { func hashimotoLight(size uint64, cache []uint32, hash []byte, nonce uint64) ([]byte, []byte) {
keccak512 := makeHasher(sha3.NewKeccak512()) keccak512 := makeHasher(sha3.NewLegacyKeccak512())
lookup := func(index uint32) []uint32 { lookup := func(index uint32) []uint32 {
rawData := generateDatasetItem(cache, index, keccak512) rawData := generateDatasetItem(cache, index, keccak512)

View file

@ -31,9 +31,9 @@ import (
"github.com/ethereum/go-ethereum/consensus/misc" "github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core/state" "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
) )
// Ethash proof-of-work protocol constants. // Ethash proof-of-work protocol constants.
@ -575,7 +575,7 @@ func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header
// SealHash returns the hash of a block prior to it being sealed. // SealHash returns the hash of a block prior to it being sealed.
func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) { func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
hasher := sha3.NewKeccak256() hasher := sha3.NewLegacyKeccak256()
rlp.Encode(hasher, []interface{}{ rlp.Encode(hasher, []interface{}{
header.ParentHash, header.ParentHash,

View file

@ -105,9 +105,37 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
return val return val
} }
// Wallet open failed, report error unless it's a PIN entry // Wallet open failed, report error unless it's a PIN entry
if !strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()) { if strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()) {
val, err = b.readPinAndReopenWallet(call)
if err == nil {
return val
}
}
// Check if the user needs to input a passphrase
if !strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPassphraseNeeded.Error()) {
throwJSException(err.Error()) throwJSException(err.Error())
} }
val, err = b.readPassphraseAndReopenWallet(call)
if err != nil {
throwJSException(err.Error())
}
return val
}
func (b *bridge) readPassphraseAndReopenWallet(call otto.FunctionCall) (otto.Value, error) {
var passwd otto.Value
wallet := call.Argument(0)
if input, err := b.prompter.PromptPassword("Please enter your passphrase: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)
}
return call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
}
func (b *bridge) readPinAndReopenWallet(call otto.FunctionCall) (otto.Value, error) {
var passwd otto.Value
wallet := call.Argument(0)
// Trezor PIN matrix input requested, display the matrix to the user and fetch the data // Trezor PIN matrix input requested, display the matrix to the user and fetch the data
fmt.Fprintf(b.printer, "Look at the device for number positions\n\n") fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
fmt.Fprintf(b.printer, "7 | 8 | 9\n") fmt.Fprintf(b.printer, "7 | 8 | 9\n")
@ -121,10 +149,7 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
} else { } else {
passwd, _ = otto.ToValue(input) passwd, _ = otto.ToValue(input)
} }
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil { return call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
throwJSException(err.Error())
}
return val
} }
// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that // UnlockAccount is a wrapper around the personal.unlockAccount RPC method that

View file

@ -48,6 +48,9 @@ import (
var ( var (
blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil) blockInsertTimer = metrics.NewRegisteredTimer("chain/inserts", nil)
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil)
ErrNoGenesis = errors.New("Genesis not found in chain") ErrNoGenesis = errors.New("Genesis not found in chain")
) )
@ -62,7 +65,7 @@ const (
triesInMemory = 128 triesInMemory = 128
// BlockChainVersion ensures that an incompatible database forces a resync from scratch. // BlockChainVersion ensures that an incompatible database forces a resync from scratch.
BlockChainVersion = 3 BlockChainVersion uint64 = 3
) )
// CacheConfig contains the configuration values for the trie caching/pruning // CacheConfig contains the configuration values for the trie caching/pruning
@ -105,7 +108,6 @@ type BlockChain struct {
scope event.SubscriptionScope scope event.SubscriptionScope
genesisBlock *types.Block genesisBlock *types.Block
mu sync.RWMutex // global mutex for locking chain operations
chainmu sync.RWMutex // blockchain insertion lock chainmu sync.RWMutex // blockchain insertion lock
procmu sync.RWMutex // block processor lock procmu sync.RWMutex // block processor lock
@ -207,6 +209,11 @@ func (bc *BlockChain) getProcInterrupt() bool {
return atomic.LoadInt32(&bc.procInterrupt) == 1 return atomic.LoadInt32(&bc.procInterrupt) == 1
} }
// GetVMConfig returns the block chain VM config.
func (bc *BlockChain) GetVMConfig() *vm.Config {
return &bc.vmConfig
}
// loadLastState loads the last known chain state from the database. This method // loadLastState loads the last known chain state from the database. This method
// assumes that the chain manager mutex is held. // assumes that the chain manager mutex is held.
func (bc *BlockChain) loadLastState() error { func (bc *BlockChain) loadLastState() error {
@ -273,8 +280,8 @@ func (bc *BlockChain) loadLastState() error {
func (bc *BlockChain) SetHead(head uint64) error { func (bc *BlockChain) SetHead(head uint64) error {
log.Warn("Rewinding blockchain", "target", head) log.Warn("Rewinding blockchain", "target", head)
bc.mu.Lock() bc.chainmu.Lock()
defer bc.mu.Unlock() defer bc.chainmu.Unlock()
// Rewind the header chain, deleting all block bodies until then // Rewind the header chain, deleting all block bodies until then
delFn := func(db rawdb.DatabaseDeleter, hash common.Hash, num uint64) { delFn := func(db rawdb.DatabaseDeleter, hash common.Hash, num uint64) {
@ -332,9 +339,9 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
return err return err
} }
// If all checks out, manually set the head block // If all checks out, manually set the head block
bc.mu.Lock() bc.chainmu.Lock()
bc.currentBlock.Store(block) bc.currentBlock.Store(block)
bc.mu.Unlock() bc.chainmu.Unlock()
log.Info("Committed new head block", "number", block.Number(), "hash", hash) log.Info("Committed new head block", "number", block.Number(), "hash", hash)
return nil return nil
@ -412,8 +419,8 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
if err := bc.SetHead(0); err != nil { if err := bc.SetHead(0); err != nil {
return err return err
} }
bc.mu.Lock() bc.chainmu.Lock()
defer bc.mu.Unlock() defer bc.chainmu.Unlock()
// Prepare the genesis block and reinitialise the chain // Prepare the genesis block and reinitialise the chain
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil { if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
@ -445,7 +452,11 @@ func (bc *BlockChain) repair(head **types.Block) error {
return nil return nil
} }
// Otherwise rewind one block and recheck state availability there // Otherwise rewind one block and recheck state availability there
(*head) = bc.GetBlock((*head).ParentHash(), (*head).NumberU64()-1) block := bc.GetBlock((*head).ParentHash(), (*head).NumberU64()-1)
if block == nil {
return fmt.Errorf("missing block %d [%x]", (*head).NumberU64()-1, (*head).ParentHash())
}
(*head) = block
} }
} }
@ -456,8 +467,8 @@ func (bc *BlockChain) Export(w io.Writer) error {
// ExportN writes a subset of the active chain to the given writer. // ExportN writes a subset of the active chain to the given writer.
func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error { func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
bc.mu.RLock() bc.chainmu.RLock()
defer bc.mu.RUnlock() defer bc.chainmu.RUnlock()
if first > last { if first > last {
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last) return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
@ -478,7 +489,6 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
reported = time.Now() reported = time.Now()
} }
} }
return nil return nil
} }
@ -634,6 +644,9 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
return nil return nil
} }
receipts := rawdb.ReadReceipts(bc.db, hash, *number) receipts := rawdb.ReadReceipts(bc.db, hash, *number)
if receipts == nil {
return nil
}
bc.receiptsCache.Add(hash, receipts) bc.receiptsCache.Add(hash, receipts)
return receipts return receipts
} }
@ -744,8 +757,8 @@ const (
// Rollback is designed to remove a chain of links from the database that aren't // Rollback is designed to remove a chain of links from the database that aren't
// certain enough to be valid. // certain enough to be valid.
func (bc *BlockChain) Rollback(chain []common.Hash) { func (bc *BlockChain) Rollback(chain []common.Hash) {
bc.mu.Lock() bc.chainmu.Lock()
defer bc.mu.Unlock() defer bc.chainmu.Unlock()
for i := len(chain) - 1; i >= 0; i-- { for i := len(chain) - 1; i >= 0; i-- {
hash := chain[i] hash := chain[i]
@ -869,7 +882,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
} }
// Update the head fast sync block if better // Update the head fast sync block if better
bc.mu.Lock() bc.chainmu.Lock()
head := blockChain[len(blockChain)-1] head := blockChain[len(blockChain)-1]
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
currentFastBlock := bc.CurrentFastBlock() currentFastBlock := bc.CurrentFastBlock()
@ -878,7 +891,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
bc.currentFastBlock.Store(head) bc.currentFastBlock.Store(head)
} }
} }
bc.mu.Unlock() bc.chainmu.Unlock()
context := []interface{}{ context := []interface{}{
"count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)), "count", stats.processed, "elapsed", common.PrettyDuration(time.Since(start)),
@ -912,6 +925,15 @@ func (bc *BlockChain) WriteBlockWithoutState(block *types.Block, td *big.Int) (e
// WriteBlockWithState writes the block and all associated state to the database. // WriteBlockWithState writes the block and all associated state to the database.
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) { func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
bc.chainmu.Lock()
defer bc.chainmu.Unlock()
return bc.writeBlockWithState(block, receipts, state)
}
// writeBlockWithState writes the block and all associated state to the database,
// but is expects the chain mutex to be held.
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
bc.wg.Add(1) bc.wg.Add(1)
defer bc.wg.Done() defer bc.wg.Done()
@ -921,9 +943,6 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
return NonStatTy, consensus.ErrUnknownAncestor return NonStatTy, consensus.ErrUnknownAncestor
} }
// Make sure no inconsistent state is leaked during insertion // Make sure no inconsistent state is leaked during insertion
bc.mu.Lock()
defer bc.mu.Unlock()
currentBlock := bc.CurrentBlock() currentBlock := bc.CurrentBlock()
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64()) localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
externTd := new(big.Int).Add(block.Difficulty(), ptd) externTd := new(big.Int).Add(block.Difficulty(), ptd)
@ -1036,6 +1055,18 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
return status, nil return status, nil
} }
// addFutureBlock checks if the block is within the max allowed window to get
// accepted for future processing, and returns an error if the block is too far
// ahead and was not added.
func (bc *BlockChain) addFutureBlock(block *types.Block) error {
max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
if block.Time().Cmp(max) > 0 {
return fmt.Errorf("future block timestamp %v > allowed %v", block.Time(), max)
}
bc.futureBlocks.Add(block.Hash(), block)
return nil
}
// InsertChain attempts to insert the given batch of blocks in to the canonical // InsertChain attempts to insert the given batch of blocks in to the canonical
// chain or, otherwise, create a fork. If an error is returned it will return // chain or, otherwise, create a fork. If an error is returned it will return
// the index number of the failing block as well an error describing what went // the index number of the failing block as well an error describing what went
@ -1043,18 +1074,9 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
// //
// After insertion is done, all accumulated events will be fired. // After insertion is done, all accumulated events will be fired.
func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) { func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
n, events, logs, err := bc.insertChain(chain)
bc.PostChainEvents(events, logs)
return n, err
}
// insertChain will execute the actual chain insertion and event aggregation. The
// only reason this method exists as a separate one is to make locking cleaner
// with deferred statements.
func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*types.Log, error) {
// Sanity check that we have something meaningful to import // Sanity check that we have something meaningful to import
if len(chain) == 0 { if len(chain) == 0 {
return 0, nil, nil, nil return 0, nil
} }
// Do a sanity check that the provided chain is actually ordered and linked // Do a sanity check that the provided chain is actually ordered and linked
for i := 1; i < len(chain); i++ { for i := 1; i < len(chain); i++ {
@ -1063,16 +1085,36 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(), log.Error("Non contiguous block insert", "number", chain[i].Number(), "hash", chain[i].Hash(),
"parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash()) "parent", chain[i].ParentHash(), "prevnumber", chain[i-1].Number(), "prevhash", chain[i-1].Hash())
return 0, nil, nil, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(), return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x…], item %d is #%d [%x…] (parent [%x…])", i-1, chain[i-1].NumberU64(),
chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4]) chain[i-1].Hash().Bytes()[:4], i, chain[i].NumberU64(), chain[i].Hash().Bytes()[:4], chain[i].ParentHash().Bytes()[:4])
} }
} }
// Pre-checks passed, start the full block imports // Pre-checks passed, start the full block imports
bc.wg.Add(1) bc.wg.Add(1)
defer bc.wg.Done()
bc.chainmu.Lock() bc.chainmu.Lock()
defer bc.chainmu.Unlock() n, events, logs, err := bc.insertChain(chain, true)
bc.chainmu.Unlock()
bc.wg.Done()
bc.PostChainEvents(events, logs)
return n, err
}
// insertChain is the internal implementation of insertChain, which assumes that
// 1) chains are contiguous, and 2) The chain mutex is held.
//
// This method is split out so that import batches that require re-injecting
// historical blocks can do so without releasing the lock, which could lead to
// racey behaviour. If a sidechain import is in progress, and the historic state
// is imported, but then new canon-head is added before the actual sidechain
// completes, then the historic state could be pruned again
func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []interface{}, []*types.Log, error) {
// If the chain is terminating, don't even bother starting u
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
return 0, nil, nil, nil
}
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
// A queued approach to delivering events. This is generally // A queued approach to delivering events. This is generally
// faster than direct delivery and requires much less mutex // faster than direct delivery and requires much less mutex
@ -1089,16 +1131,56 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
for i, block := range chain { for i, block := range chain {
headers[i] = block.Header() headers[i] = block.Header()
seals[i] = true seals[i] = verifySeals
} }
abort, results := bc.engine.VerifyHeaders(bc, headers, seals) abort, results := bc.engine.VerifyHeaders(bc, headers, seals)
defer close(abort) defer close(abort)
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss) // Peek the error for the first block to decide the directing import logic
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain) it := newInsertIterator(chain, results, bc.Validator())
// Iterate over the blocks and insert when the verifier permits block, err := it.next()
for i, block := range chain { switch {
// First block is pruned, insert as sidechain and reorg only if TD grows enough
case err == consensus.ErrPrunedAncestor:
return bc.insertSidechain(it)
// First block is future, shove it (and all children) to the future queue (unknown ancestor)
case err == consensus.ErrFutureBlock || (err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(it.first().ParentHash())):
for block != nil && (it.index == 0 || err == consensus.ErrUnknownAncestor) {
if err := bc.addFutureBlock(block); err != nil {
return it.index, events, coalescedLogs, err
}
block, err = it.next()
}
stats.queued += it.processed()
stats.ignored += it.remaining()
// If there are any still remaining, mark as ignored
return it.index, events, coalescedLogs, err
// First block (and state) is known
// 1. We did a roll-back, and should now do a re-import
// 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
// from the canonical chain, which has not been verified.
case err == ErrKnownBlock:
// Skip all known blocks that behind us
current := bc.CurrentBlock().NumberU64()
for block != nil && err == ErrKnownBlock && current >= block.NumberU64() {
stats.ignored++
block, err = it.next()
}
// Falls through to the block import
// Some other error occurred, abort
case err != nil:
stats.ignored += len(it.chain)
bc.reportBlock(block, nil, err)
return it.index, events, coalescedLogs, err
}
// No validation errors for the first block (or chain prefix skipped)
for ; block != nil && err == nil; block, err = it.next() {
// If the chain is terminating, stop processing blocks // If the chain is terminating, stop processing blocks
if atomic.LoadInt32(&bc.procInterrupt) == 1 { if atomic.LoadInt32(&bc.procInterrupt) == 1 {
log.Debug("Premature abort during blocks processing") log.Debug("Premature abort during blocks processing")
@ -1107,115 +1189,53 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
// If the header is a banned one, straight out abort // If the header is a banned one, straight out abort
if BadHashes[block.Hash()] { if BadHashes[block.Hash()] {
bc.reportBlock(block, nil, ErrBlacklistedHash) bc.reportBlock(block, nil, ErrBlacklistedHash)
return i, events, coalescedLogs, ErrBlacklistedHash return it.index, events, coalescedLogs, ErrBlacklistedHash
} }
// Wait for the block's verification to complete // Retrieve the parent block and it's state to execute on top
bstart := time.Now() start := time.Now()
err := <-results parent := it.previous()
if err == nil { if parent == nil {
err = bc.Validator().ValidateBody(block)
}
switch {
case err == ErrKnownBlock:
// Block and state both already known. However if the current block is below
// this number we did a rollback and we should reimport it nonetheless.
if bc.CurrentBlock().NumberU64() >= block.NumberU64() {
stats.ignored++
continue
}
case err == consensus.ErrFutureBlock:
// Allow up to MaxFuture second in the future blocks. If this limit is exceeded
// the chain is discarded and processed at a later time if given.
max := big.NewInt(time.Now().Unix() + maxTimeFutureBlocks)
if block.Time().Cmp(max) > 0 {
return i, events, coalescedLogs, fmt.Errorf("future block: %v > %v", block.Time(), max)
}
bc.futureBlocks.Add(block.Hash(), block)
stats.queued++
continue
case err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(block.ParentHash()):
bc.futureBlocks.Add(block.Hash(), block)
stats.queued++
continue
case err == consensus.ErrPrunedAncestor:
// Block competing with the canonical chain, store in the db, but don't process
// until the competitor TD goes above the canonical TD
currentBlock := bc.CurrentBlock()
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty())
if localTd.Cmp(externTd) > 0 {
if err = bc.WriteBlockWithoutState(block, externTd); err != nil {
return i, events, coalescedLogs, err
}
continue
}
// Competitor chain beat canonical, gather all blocks from the common ancestor
var winner []*types.Block
parent := bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
for !bc.HasState(parent.Root()) {
winner = append(winner, parent)
parent = bc.GetBlock(parent.ParentHash(), parent.NumberU64()-1)
}
for j := 0; j < len(winner)/2; j++ {
winner[j], winner[len(winner)-1-j] = winner[len(winner)-1-j], winner[j]
}
// Import all the pruned blocks to make the state available
bc.chainmu.Unlock()
_, evs, logs, err := bc.insertChain(winner)
bc.chainmu.Lock()
events, coalescedLogs = evs, logs
if err != nil {
return i, events, coalescedLogs, err
}
case err != nil:
bc.reportBlock(block, nil, err)
return i, events, coalescedLogs, err
}
// Create a new statedb using the parent block and report an
// error if it fails.
var parent *types.Block
if i == 0 {
parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1) parent = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
} else {
parent = chain[i-1]
} }
state, err := state.New(parent.Root(), bc.stateCache) state, err := state.New(parent.Root(), bc.stateCache)
if err != nil { if err != nil {
return i, events, coalescedLogs, err return it.index, events, coalescedLogs, err
} }
// Process block using the parent state as reference point. // Process block using the parent state as reference point.
t0 := time.Now()
receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig) receipts, logs, usedGas, err := bc.processor.Process(block, state, bc.vmConfig)
t1 := time.Now()
if err != nil { if err != nil {
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
return i, events, coalescedLogs, err return it.index, events, coalescedLogs, err
} }
// Validate the state using the default validator // Validate the state using the default validator
err = bc.Validator().ValidateState(block, parent, state, receipts, usedGas) if err := bc.Validator().ValidateState(block, parent, state, receipts, usedGas); err != nil {
if err != nil {
bc.reportBlock(block, receipts, err) bc.reportBlock(block, receipts, err)
return i, events, coalescedLogs, err return it.index, events, coalescedLogs, err
} }
proctime := time.Since(bstart) t2 := time.Now()
proctime := time.Since(start)
// Write the block to the chain and get the status. // Write the block to the chain and get the status.
status, err := bc.WriteBlockWithState(block, receipts, state) status, err := bc.writeBlockWithState(block, receipts, state)
t3 := time.Now()
if err != nil { if err != nil {
return i, events, coalescedLogs, err return it.index, events, coalescedLogs, err
} }
blockInsertTimer.UpdateSince(start)
blockExecutionTimer.Update(t1.Sub(t0))
blockValidationTimer.Update(t2.Sub(t1))
blockWriteTimer.Update(t3.Sub(t2))
switch status { switch status {
case CanonStatTy: case CanonStatTy:
log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(), "uncles", len(block.Uncles()), log.Debug("Inserted new block", "number", block.Number(), "hash", block.Hash(),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "elapsed", common.PrettyDuration(time.Since(bstart))) "uncles", len(block.Uncles()), "txs", len(block.Transactions()), "gas", block.GasUsed(),
"elapsed", common.PrettyDuration(time.Since(start)),
"root", block.Root())
coalescedLogs = append(coalescedLogs, logs...) coalescedLogs = append(coalescedLogs, logs...)
blockInsertTimer.UpdateSince(bstart)
events = append(events, ChainEvent{block, block.Hash(), logs}) events = append(events, ChainEvent{block, block.Hash(), logs})
lastCanon = block lastCanon = block
@ -1223,78 +1243,153 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
bc.gcproc += proctime bc.gcproc += proctime
case SideStatTy: case SideStatTy:
log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(), "diff", block.Difficulty(), "elapsed", log.Debug("Inserted forked block", "number", block.Number(), "hash", block.Hash(),
common.PrettyDuration(time.Since(bstart)), "txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles())) "diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
blockInsertTimer.UpdateSince(bstart) "root", block.Root())
events = append(events, ChainSideEvent{block}) events = append(events, ChainSideEvent{block})
} }
blockInsertTimer.UpdateSince(start)
stats.processed++ stats.processed++
stats.usedGas += usedGas stats.usedGas += usedGas
cache, _ := bc.stateCache.TrieDB().Size() cache, _ := bc.stateCache.TrieDB().Size()
stats.report(chain, i, cache) stats.report(chain, it.index, cache)
} }
// Any blocks remaining here? The only ones we care about are the future ones
if block != nil && err == consensus.ErrFutureBlock {
if err := bc.addFutureBlock(block); err != nil {
return it.index, events, coalescedLogs, err
}
block, err = it.next()
for ; block != nil && err == consensus.ErrUnknownAncestor; block, err = it.next() {
if err := bc.addFutureBlock(block); err != nil {
return it.index, events, coalescedLogs, err
}
stats.queued++
}
}
stats.ignored += it.remaining()
// Append a single chain head event if we've progressed the chain // Append a single chain head event if we've progressed the chain
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() { if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
events = append(events, ChainHeadEvent{lastCanon}) events = append(events, ChainHeadEvent{lastCanon})
} }
return 0, events, coalescedLogs, nil return it.index, events, coalescedLogs, err
} }
// insertStats tracks and reports on block insertion. // insertSidechain is called when an import batch hits upon a pruned ancestor
type insertStats struct { // error, which happens when a sidechain with a sufficiently old fork-block is
queued, processed, ignored int // found.
usedGas uint64 //
lastIndex int // The method writes all (header-and-body-valid) blocks to disk, then tries to
startTime mclock.AbsTime // switch over to the new chain if the TD exceeded the current chain.
} func (bc *BlockChain) insertSidechain(it *insertIterator) (int, []interface{}, []*types.Log, error) {
// statsReportLimit is the time limit during import and export after which we
// always print out progress. This avoids the user wondering what's going on.
const statsReportLimit = 8 * time.Second
// report prints statistics if some number of blocks have been processed
// or more than a few seconds have passed since the last message.
func (st *insertStats) report(chain []*types.Block, index int, cache common.StorageSize) {
// Fetch the timings for the batch
var ( var (
now = mclock.Now() externTd *big.Int
elapsed = time.Duration(now) - time.Duration(st.startTime) current = bc.CurrentBlock()
) )
// If we're at the last block of the batch or report period reached, log // The first sidechain block error is already verified to be ErrPrunedAncestor.
if index == len(chain)-1 || elapsed >= statsReportLimit { // Since we don't import them here, we expect ErrUnknownAncestor for the remaining
// ones. Any other errors means that the block is invalid, and should not be written
// to disk.
block, err := it.current(), consensus.ErrPrunedAncestor
for ; block != nil && (err == consensus.ErrPrunedAncestor); block, err = it.next() {
// Check the canonical state root for that number
if number := block.NumberU64(); current.NumberU64() >= number {
if canonical := bc.GetBlockByNumber(number); canonical != nil && canonical.Root() == block.Root() {
// This is most likely a shadow-state attack. When a fork is imported into the
// database, and it eventually reaches a block height which is not pruned, we
// just found that the state already exist! This means that the sidechain block
// refers to a state which already exists in our canon chain.
//
// If left unchecked, we would now proceed importing the blocks, without actually
// having verified the state of the previous blocks.
log.Warn("Sidechain ghost-state attack detected", "number", block.NumberU64(), "sideroot", block.Root(), "canonroot", canonical.Root())
// If someone legitimately side-mines blocks, they would still be imported as usual. However,
// we cannot risk writing unverified blocks to disk when they obviously target the pruning
// mechanism.
return it.index, nil, nil, errors.New("sidechain ghost-state attack")
}
}
if externTd == nil {
externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1)
}
externTd = new(big.Int).Add(externTd, block.Difficulty())
if !bc.HasBlock(block.Hash(), block.NumberU64()) {
start := time.Now()
if err := bc.WriteBlockWithoutState(block, externTd); err != nil {
return it.index, nil, nil, err
}
log.Debug("Inserted sidechain block", "number", block.Number(), "hash", block.Hash(),
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
"root", block.Root())
}
}
// At this point, we've written all sidechain blocks to database. Loop ended
// either on some other error or all were processed. If there was some other
// error, we can ignore the rest of those blocks.
//
// If the externTd was larger than our local TD, we now need to reimport the previous
// blocks to regenerate the required state
localTd := bc.GetTd(current.Hash(), current.NumberU64())
if localTd.Cmp(externTd) > 0 {
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().NumberU64(), "sidetd", externTd, "localtd", localTd)
return it.index, nil, nil, err
}
// Gather all the sidechain hashes (full blocks may be memory heavy)
var ( var (
end = chain[index] hashes []common.Hash
txs = countTransactions(chain[st.lastIndex : index+1]) numbers []uint64
) )
context := []interface{}{ parent := bc.GetHeader(it.previous().Hash(), it.previous().NumberU64())
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000, for parent != nil && !bc.HasState(parent.Root) {
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed), hashes = append(hashes, parent.Hash())
"number", end.Number(), "hash", end.Hash(), numbers = append(numbers, parent.Number.Uint64())
}
if timestamp := time.Unix(end.Time().Int64(), 0); time.Since(timestamp) > time.Minute {
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
}
context = append(context, []interface{}{"cache", cache}...)
if st.queued > 0 { parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1)
context = append(context, []interface{}{"queued", st.queued}...)
} }
if st.ignored > 0 { if parent == nil {
context = append(context, []interface{}{"ignored", st.ignored}...) return it.index, nil, nil, errors.New("missing parent")
} }
log.Info("Imported new chain segment", context...) // Import all the pruned blocks to make the state available
var (
blocks []*types.Block
memory common.StorageSize
)
for i := len(hashes) - 1; i >= 0; i-- {
// Append the next block to our batch
block := bc.GetBlock(hashes[i], numbers[i])
*st = insertStats{startTime: now, lastIndex: index + 1} blocks = append(blocks, block)
} memory += block.Size()
}
func countTransactions(chain []*types.Block) (c int) { // If memory use grew too large, import and continue. Sadly we need to discard
for _, b := range chain { // all raised events and logs from notifications since we're too heavy on the
c += len(b.Transactions()) // memory here.
if len(blocks) >= 2048 || memory > 64*1024*1024 {
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
if _, _, _, err := bc.insertChain(blocks, false); err != nil {
return 0, nil, nil, err
} }
return c blocks, memory = blocks[:0], 0
// If the chain is terminating, stop processing blocks
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
log.Debug("Premature abort during blocks processing")
return 0, nil, nil, nil
}
}
}
if len(blocks) > 0 {
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
return bc.insertChain(blocks, false)
}
return 0, nil, nil, nil
} }
// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them // reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
@ -1469,8 +1564,10 @@ func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, e
bc.addBadBlock(block) bc.addBadBlock(block)
var receiptString string var receiptString string
for _, receipt := range receipts { for i, receipt := range receipts {
receiptString += fmt.Sprintf("\t%v\n", receipt) receiptString += fmt.Sprintf("\t %d: cumulative: %v gas: %v contract: %v status: %v tx: %v logs: %v bloom: %x state: %x\n",
i, receipt.CumulativeGasUsed, receipt.GasUsed, receipt.ContractAddress.Hex(),
receipt.Status, receipt.TxHash.Hex(), receipt.Logs, receipt.Bloom, receipt.PostState)
} }
log.Error(fmt.Sprintf(` log.Error(fmt.Sprintf(`
########## BAD BLOCK ######### ########## BAD BLOCK #########
@ -1507,36 +1604,12 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
defer bc.wg.Done() defer bc.wg.Done()
whFunc := func(header *types.Header) error { whFunc := func(header *types.Header) error {
bc.mu.Lock()
defer bc.mu.Unlock()
_, err := bc.hc.WriteHeader(header) _, err := bc.hc.WriteHeader(header)
return err return err
} }
return bc.hc.InsertHeaderChain(chain, whFunc, start) return bc.hc.InsertHeaderChain(chain, whFunc, start)
} }
// writeHeader writes a header into the local chain, given that its parent is
// already known. If the total difficulty of the newly inserted header becomes
// greater than the current known TD, the canonical chain is re-routed.
//
// Note: This method is not concurrent-safe with inserting blocks simultaneously
// into the chain, as side effects caused by reorganisations cannot be emulated
// without the real blocks. Hence, writing headers directly should only be done
// in two scenarios: pure-header mode of operation (light clients), or properly
// separated header/block phases (non-archive clients).
func (bc *BlockChain) writeHeader(header *types.Header) error {
bc.wg.Add(1)
defer bc.wg.Done()
bc.mu.Lock()
defer bc.mu.Unlock()
_, err := bc.hc.WriteHeader(header)
return err
}
// CurrentHeader retrieves the current head header of the canonical chain. The // CurrentHeader retrieves the current head header of the canonical chain. The
// header is retrieved from the HeaderChain's internal cache. // header is retrieved from the HeaderChain's internal cache.
func (bc *BlockChain) CurrentHeader() *types.Header { func (bc *BlockChain) CurrentHeader() *types.Header {
@ -1585,8 +1658,8 @@ func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []com
// //
// Note: ancestor == 0 returns the same block, 1 returns its parent and so on. // Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) { func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
bc.chainmu.Lock() bc.chainmu.RLock()
defer bc.chainmu.Unlock() defer bc.chainmu.RUnlock()
return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical) return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
} }

143
core/blockchain_insert.go Normal file
View file

@ -0,0 +1,143 @@
// Copyright 2018 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 core
import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
// insertStats tracks and reports on block insertion.
type insertStats struct {
queued, processed, ignored int
usedGas uint64
lastIndex int
startTime mclock.AbsTime
}
// statsReportLimit is the time limit during import and export after which we
// always print out progress. This avoids the user wondering what's going on.
const statsReportLimit = 8 * time.Second
// report prints statistics if some number of blocks have been processed
// or more than a few seconds have passed since the last message.
func (st *insertStats) report(chain []*types.Block, index int, cache common.StorageSize) {
// Fetch the timings for the batch
var (
now = mclock.Now()
elapsed = time.Duration(now) - time.Duration(st.startTime)
)
// If we're at the last block of the batch or report period reached, log
if index == len(chain)-1 || elapsed >= statsReportLimit {
// Count the number of transactions in this segment
var txs int
for _, block := range chain[st.lastIndex : index+1] {
txs += len(block.Transactions())
}
end := chain[index]
// Assemble the log context and send it to the logger
context := []interface{}{
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
"number", end.Number(), "hash", end.Hash(),
}
if timestamp := time.Unix(end.Time().Int64(), 0); time.Since(timestamp) > time.Minute {
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
}
context = append(context, []interface{}{"cache", cache}...)
if st.queued > 0 {
context = append(context, []interface{}{"queued", st.queued}...)
}
if st.ignored > 0 {
context = append(context, []interface{}{"ignored", st.ignored}...)
}
log.Info("Imported new chain segment", context...)
// Bump the stats reported to the next section
*st = insertStats{startTime: now, lastIndex: index + 1}
}
}
// insertIterator is a helper to assist during chain import.
type insertIterator struct {
chain types.Blocks
results <-chan error
index int
validator Validator
}
// newInsertIterator creates a new iterator based on the given blocks, which are
// assumed to be a contiguous chain.
func newInsertIterator(chain types.Blocks, results <-chan error, validator Validator) *insertIterator {
return &insertIterator{
chain: chain,
results: results,
index: -1,
validator: validator,
}
}
// next returns the next block in the iterator, along with any potential validation
// error for that block. When the end is reached, it will return (nil, nil).
func (it *insertIterator) next() (*types.Block, error) {
if it.index+1 >= len(it.chain) {
it.index = len(it.chain)
return nil, nil
}
it.index++
if err := <-it.results; err != nil {
return it.chain[it.index], err
}
return it.chain[it.index], it.validator.ValidateBody(it.chain[it.index])
}
// current returns the current block that's being processed.
func (it *insertIterator) current() *types.Block {
if it.index < 0 || it.index+1 >= len(it.chain) {
return nil
}
return it.chain[it.index]
}
// previous returns the previous block was being processed, or nil
func (it *insertIterator) previous() *types.Block {
if it.index < 1 {
return nil
}
return it.chain[it.index-1]
}
// first returns the first block in the it.
func (it *insertIterator) first() *types.Block {
return it.chain[0]
}
// remaining returns the number of remaining blocks.
func (it *insertIterator) remaining() int {
return len(it.chain) - it.index
}
// processed returns the number of processed blocks.
func (it *insertIterator) processed() int {
return it.index + 1
}

View file

@ -162,11 +162,11 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
blockchain.reportBlock(block, receipts, err) blockchain.reportBlock(block, receipts, err)
return err return err
} }
blockchain.mu.Lock() blockchain.chainmu.Lock()
rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash()))) rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
rawdb.WriteBlock(blockchain.db, block) rawdb.WriteBlock(blockchain.db, block)
statedb.Commit(false) statedb.Commit(false)
blockchain.mu.Unlock() blockchain.chainmu.Unlock()
} }
return nil return nil
} }
@ -180,10 +180,10 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
return err return err
} }
// Manually insert the header into the database, but don't reorganise (allows subsequent testing) // Manually insert the header into the database, but don't reorganise (allows subsequent testing)
blockchain.mu.Lock() blockchain.chainmu.Lock()
rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash))) rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
rawdb.WriteHeader(blockchain.db, header) rawdb.WriteHeader(blockchain.db, header)
blockchain.mu.Unlock() blockchain.chainmu.Unlock()
} }
return nil return nil
} }
@ -579,11 +579,11 @@ func testInsertNonceError(t *testing.T, full bool) {
blockchain.hc.engine = blockchain.engine blockchain.hc.engine = blockchain.engine
failRes, err = blockchain.InsertHeaderChain(headers, 1) failRes, err = blockchain.InsertHeaderChain(headers, 1)
} }
// Check that the returned error indicates the failure. // Check that the returned error indicates the failure
if failRes != failAt { if failRes != failAt {
t.Errorf("test %d: failure index mismatch: have %d, want %d", i, failRes, failAt) t.Errorf("test %d: failure (%v) index mismatch: have %d, want %d", i, err, failRes, failAt)
} }
// Check that all no blocks after the failing block have been inserted. // Check that all blocks after the failing block have been inserted
for j := 0; j < i-failAt; j++ { for j := 0; j < i-failAt; j++ {
if full { if full {
if block := blockchain.GetBlockByNumber(failNum + uint64(j)); block != nil { if block := blockchain.GetBlockByNumber(failNum + uint64(j)); block != nil {
@ -1345,7 +1345,7 @@ func TestLargeReorgTrieGC(t *testing.T) {
t.Fatalf("failed to insert shared chain: %v", err) t.Fatalf("failed to insert shared chain: %v", err)
} }
if _, err := chain.InsertChain(original); err != nil { if _, err := chain.InsertChain(original); err != nil {
t.Fatalf("failed to insert shared chain: %v", err) t.Fatalf("failed to insert original chain: %v", err)
} }
// Ensure that the state associated with the forking point is pruned away // Ensure that the state associated with the forking point is pruned away
if node, _ := chain.stateCache.TrieDB().Node(shared[len(shared)-1].Root()); node != nil { if node, _ := chain.stateCache.TrieDB().Node(shared[len(shared)-1].Root()); node != nil {

View file

@ -151,6 +151,9 @@ func (e *GenesisMismatchError) Error() string {
// //
// The returned chain configuration is never nil. // The returned chain configuration is never nil.
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) { func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlockWithOverride(db, genesis, nil)
}
func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, constantinopleOverride *big.Int) (*params.ChainConfig, common.Hash, error) {
if genesis != nil && genesis.Config == nil { if genesis != nil && genesis.Config == nil {
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
} }
@ -178,6 +181,10 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
// Get the existing chain configuration. // Get the existing chain configuration.
newcfg := genesis.configOrDefault(stored) newcfg := genesis.configOrDefault(stored)
if constantinopleOverride != nil {
newcfg.ConstantinopleBlock = constantinopleOverride
newcfg.PetersburgBlock = constantinopleOverride
}
storedcfg := rawdb.ReadChainConfig(db, stored) storedcfg := rawdb.ReadChainConfig(db, stored)
if storedcfg == nil { if storedcfg == nil {
log.Warn("Found genesis block without chain config") log.Warn("Found genesis block without chain config")

View file

@ -219,6 +219,8 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
// Generate the list of seal verification requests, and start the parallel verifier // Generate the list of seal verification requests, and start the parallel verifier
seals := make([]bool, len(chain)) seals := make([]bool, len(chain))
if checkFreq != 0 {
// In case of checkFreq == 0 all seals are left false.
for i := 0; i < len(seals)/checkFreq; i++ { for i := 0; i < len(seals)/checkFreq; i++ {
index := i*checkFreq + hc.rand.Intn(checkFreq) index := i*checkFreq + hc.rand.Intn(checkFreq)
if index >= len(seals) { if index >= len(seals) {
@ -226,7 +228,9 @@ func (hc *HeaderChain) ValidateHeaderChain(chain []*types.Header, checkFreq int)
} }
seals[index] = true seals[index] = true
} }
seals[len(seals)-1] = true // Last should always be verified to avoid junk // Last should always be verified to avoid junk.
seals[len(seals)-1] = true
}
abort, results := hc.engine.VerifyHeaders(hc, chain, seals) abort, results := hc.engine.VerifyHeaders(hc, chain, seals)
defer close(abort) defer close(abort)

View file

@ -23,9 +23,9 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
) )
// Tests block header storage and retrieval operations. // Tests block header storage and retrieval operations.
@ -47,7 +47,7 @@ func TestHeaderStorage(t *testing.T) {
if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil { if entry := ReadHeaderRLP(db, header.Hash(), header.Number.Uint64()); entry == nil {
t.Fatalf("Stored header RLP not found") t.Fatalf("Stored header RLP not found")
} else { } else {
hasher := sha3.NewKeccak256() hasher := sha3.NewLegacyKeccak256()
hasher.Write(entry) hasher.Write(entry)
if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() { if hash := common.BytesToHash(hasher.Sum(nil)); hash != header.Hash() {
@ -68,7 +68,7 @@ func TestBodyStorage(t *testing.T) {
// Create a test body to move around the database and make sure it's really new // Create a test body to move around the database and make sure it's really new
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}} body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
hasher := sha3.NewKeccak256() hasher := sha3.NewLegacyKeccak256()
rlp.Encode(hasher, body) rlp.Encode(hasher, body)
hash := common.BytesToHash(hasher.Sum(nil)) hash := common.BytesToHash(hasher.Sum(nil))
@ -85,7 +85,7 @@ func TestBodyStorage(t *testing.T) {
if entry := ReadBodyRLP(db, hash, 0); entry == nil { if entry := ReadBodyRLP(db, hash, 0); entry == nil {
t.Fatalf("Stored body RLP not found") t.Fatalf("Stored body RLP not found")
} else { } else {
hasher := sha3.NewKeccak256() hasher := sha3.NewLegacyKeccak256()
hasher.Write(entry) hasher.Write(entry)
if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash { if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {

View file

@ -26,19 +26,27 @@ import (
) )
// ReadDatabaseVersion retrieves the version number of the database. // ReadDatabaseVersion retrieves the version number of the database.
func ReadDatabaseVersion(db DatabaseReader) int { func ReadDatabaseVersion(db DatabaseReader) *uint64 {
var version int var version uint64
enc, _ := db.Get(databaseVerisionKey) enc, _ := db.Get(databaseVerisionKey)
rlp.DecodeBytes(enc, &version) if len(enc) == 0 {
return nil
}
if err := rlp.DecodeBytes(enc, &version); err != nil {
return nil
}
return version return &version
} }
// WriteDatabaseVersion stores the version number of the database // WriteDatabaseVersion stores the version number of the database
func WriteDatabaseVersion(db DatabaseWriter, version int) { func WriteDatabaseVersion(db DatabaseWriter, version uint64) {
enc, _ := rlp.EncodeToBytes(version) enc, err := rlp.EncodeToBytes(version)
if err := db.Put(databaseVerisionKey, enc); err != nil { if err != nil {
log.Crit("Failed to encode database version", "err", err)
}
if err = db.Put(databaseVerisionKey, enc); err != nil {
log.Crit("Failed to store the database version", "err", err) log.Crit("Failed to store the database version", "err", err)
} }
} }

View file

@ -468,9 +468,9 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObjec
// //
// Carrying over the balance ensures that Ether doesn't disappear. // Carrying over the balance ensures that Ether doesn't disappear.
func (self *StateDB) CreateAccount(addr common.Address) { func (self *StateDB) CreateAccount(addr common.Address) {
new, prev := self.createObject(addr) newObj, prev := self.createObject(addr)
if prev != nil { if prev != nil {
new.setBalance(prev.data.Balance) newObj.setBalance(prev.data.Balance)
} }
} }

View file

@ -22,7 +22,7 @@ import (
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
) )
// senderCacher is a concurrent transaction sender recoverer anc cacher. // senderCacher is a concurrent transaction sender recoverer and cacher.
var senderCacher = newTxSenderCacher(runtime.NumCPU()) var senderCacher = newTxSenderCacher(runtime.NumCPU())
// txSenderCacherRequest is a request for recovering transaction senders with a // txSenderCacherRequest is a request for recovering transaction senders with a

View file

@ -172,6 +172,26 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump) log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", DefaultTxPoolConfig.PriceBump)
conf.PriceBump = DefaultTxPoolConfig.PriceBump conf.PriceBump = DefaultTxPoolConfig.PriceBump
} }
if conf.AccountSlots < 1 {
log.Warn("Sanitizing invalid txpool account slots", "provided", conf.AccountSlots, "updated", DefaultTxPoolConfig.AccountSlots)
conf.AccountSlots = DefaultTxPoolConfig.AccountSlots
}
if conf.GlobalSlots < 1 {
log.Warn("Sanitizing invalid txpool global slots", "provided", conf.GlobalSlots, "updated", DefaultTxPoolConfig.GlobalSlots)
conf.GlobalSlots = DefaultTxPoolConfig.GlobalSlots
}
if conf.AccountQueue < 1 {
log.Warn("Sanitizing invalid txpool account queue", "provided", conf.AccountQueue, "updated", DefaultTxPoolConfig.AccountQueue)
conf.AccountQueue = DefaultTxPoolConfig.AccountQueue
}
if conf.GlobalQueue < 1 {
log.Warn("Sanitizing invalid txpool global queue", "provided", conf.GlobalQueue, "updated", DefaultTxPoolConfig.GlobalQueue)
conf.GlobalQueue = DefaultTxPoolConfig.GlobalQueue
}
if conf.Lifetime < 1 {
log.Warn("Sanitizing invalid txpool lifetime", "provided", conf.Lifetime, "updated", DefaultTxPoolConfig.Lifetime)
conf.Lifetime = DefaultTxPoolConfig.Lifetime
}
return conf return conf
} }
@ -825,7 +845,7 @@ func (pool *TxPool) addTxs(txs []*types.Transaction, local bool) []error {
// addTxsLocked attempts to queue a batch of transactions if they are valid, // addTxsLocked attempts to queue a batch of transactions if they are valid,
// whilst assuming the transaction pool lock is already held. // whilst assuming the transaction pool lock is already held.
func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) []error { func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) []error {
// Add the batch of transaction, tracking the accepted ones // Add the batch of transactions, tracking the accepted ones
dirty := make(map[common.Address]struct{}) dirty := make(map[common.Address]struct{})
errs := make([]error, len(txs)) errs := make([]error, len(txs))

Some files were not shown because too many files have changed in this diff Show more