Compare commits
143 Commits
Author | SHA1 | Date | |
---|---|---|---|
c942700427 | |||
cde35439e0 | |||
4f908db69e | |||
320d132925 | |||
7ae2a7bd84 | |||
fd34bf594c | |||
996230174c | |||
8857707606 | |||
d6c1fcbe04 | |||
79cac793c0 | |||
5de6b6b529 | |||
3d2bedf8d0 | |||
8ea3d8ad90 | |||
a0127019c3 | |||
7a333e4104 | |||
799fe99537 | |||
3b02b0ba4b | |||
85217b08bd | |||
dcff622d43 | |||
a3db00f270 | |||
769e43e334 | |||
8d8ddea1a3 | |||
068725c5b0 | |||
710775f435 | |||
0fd0108507 | |||
3c62cc6bba | |||
333b1bfb6c | |||
d1ace4f344 | |||
637a75d61a | |||
355d55bd34 | |||
7038b5734c | |||
1ecf2860cf | |||
034f65e9e8 | |||
607a1968e6 | |||
3f54994db0 | |||
2695aa9e0d | |||
e247dcc141 | |||
b774d0a507 | |||
4976fcc91a | |||
878aa58ec6 | |||
475a0664c5 | |||
4625b1257f | |||
21d54bcaac | |||
7383db4dac | |||
afb65f6ace | |||
1f1c751b6e | |||
a3f31f51f3 | |||
e63995b3f3 | |||
dd3e894747 | |||
df355eceb4 | |||
84cb00a94d | |||
992a7bbad5 | |||
a458153098 | |||
fe5258b41e | |||
d9be337669 | |||
7bd6f39dc3 | |||
b247052a64 | |||
276f824707 | |||
048b463b30 | |||
9f5fb15097 | |||
2072c26a96 | |||
4da2092908 | |||
3ab9dcc3bd | |||
18f702faf7 | |||
3a95128b22 | |||
631e2f07f6 | |||
7fa3509e2e | |||
86ec742f97 | |||
d9a07fba67 | |||
4cd90e02e2 | |||
1f3dfed19e | |||
2ae481ff6b | |||
c7664b0636 | |||
9dc5d1a915 | |||
c03f694be5 | |||
2a2fd5adf8 | |||
115b1c38ac | |||
4aeeecfded | |||
1636d9574b | |||
88168ff5c5 | |||
d5cad488be | |||
2eb838ed97 | |||
38cce9ac33 | |||
7240f4d800 | |||
7ca40306af | |||
6df3e4eeb0 | |||
d70c4faf20 | |||
81f04fa606 | |||
ae857e74bf | |||
56a3f6c03c | |||
356c49fa7e | |||
428eabe28d | |||
e05d468075 | |||
aca588a8e4 | |||
fe03b76ffe | |||
072c95fb74 | |||
e8ff318205 | |||
c1c4301121 | |||
391d4cb9b5 | |||
3f421aca54 | |||
8ec344bf60 | |||
33d233d3e1 | |||
49975264a8 | |||
1ea5279d5d | |||
27913dd226 | |||
ddaf48bf84 | |||
57a90ad450 | |||
1d284c201d | |||
b025053ab0 | |||
9bfd0b60cc | |||
a4af734328 | |||
6537ab5dd3 | |||
735343430d | |||
9e9fc87e70 | |||
335760bf06 | |||
7df52e324c | |||
5e4fd8e7db | |||
880de230b4 | |||
81c3dc728f | |||
ca7c13ba8f | |||
e1edfe0689 | |||
27ce4eb78b | |||
5f251a6448 | |||
fe86a707d8 | |||
b01cfce362 | |||
de4265fa02 | |||
90ea542e9e | |||
472c23a801 | |||
d322c9d550 | |||
3ad73443c7 | |||
7dbb075c07 | |||
aebf9e2fe7 | |||
aad3c67a92 | |||
fe26b2f366 | |||
88d7d4fed4 | |||
9940d93a43 | |||
3796751efc | |||
e79821cabe | |||
e57e4571d3 | |||
b3be9b7cd8 | |||
4e6f53ac33 | |||
ebbf3dfafb | |||
1e190a3b1c |
24
.github/CODEOWNERS
vendored
24
.github/CODEOWNERS
vendored
@ -2,6 +2,7 @@
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
accounts/usbwallet @karalabe
|
||||
accounts/abi @gballet
|
||||
consensus @karalabe
|
||||
core/ @karalabe @holiman
|
||||
eth/ @karalabe
|
||||
@ -9,27 +10,4 @@ les/ @zsfelfoldi
|
||||
light/ @zsfelfoldi
|
||||
mobile/ @karalabe
|
||||
p2p/ @fjl @zsfelfoldi
|
||||
p2p/simulations @lmars
|
||||
p2p/protocols @zelig
|
||||
swarm/api/http @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
|
||||
|
2
.github/no-response.yml
vendored
2
.github/no-response.yml
vendored
@ -1,7 +1,7 @@
|
||||
# Number of days of inactivity before an Issue is closed for lack of response
|
||||
daysUntilClose: 30
|
||||
# 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
|
||||
closeComment: >
|
||||
This issue has been automatically closed because there has been no response
|
||||
|
2
.github/stale.yml
vendored
2
.github/stale.yml
vendored
@ -7,7 +7,7 @@ exemptLabels:
|
||||
- pinned
|
||||
- security
|
||||
# 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
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
|
@ -68,8 +68,11 @@ matrix:
|
||||
- debhelper
|
||||
- dput
|
||||
- fakeroot
|
||||
- python-bzrlib
|
||||
- python-paramiko
|
||||
script:
|
||||
- go run build/ci.go debsrc -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>" -upload ppa:ethereum/ethereum
|
||||
- echo '|1|7SiYPr9xl3uctzovOTj4gMwAC1M=|t6ReES75Bo/PxlOPJ6/GsGbTrM0= ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA0aKz5UTUndYgIGG7dQBV+HaeuEZJ2xPHo2DS2iSKvUL4xNMSAY4UguNW+pX56nAQmZKIZZ8MaEvSj6zMEDiq6HFfn5JcTlM80UwlnyKe8B8p7Nk06PPQLrnmQt5fh0HmEcZx+JU9TZsfCHPnX7MNz4ELfZE6cFsclClrKim3BHUIGq//t93DllB+h4O9LHjEUsQ1Sr63irDLSutkLJD6RXchjROXkNirlcNVHH/jwLWR5RcYilNX7S5bIkK8NlWPjsn/8Ua5O7I9/YoE97PpO6i73DTGLh5H9JN/SITwCKBkgSDWUt61uPK3Y11Gty7o2lWsBjhBUm2Y38CBsoGmBw==' >> ~/.ssh/known_hosts
|
||||
- go run build/ci.go debsrc -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
|
||||
|
||||
# This builder does the Linux Azure uploads
|
||||
- if: type = push
|
||||
@ -156,7 +159,7 @@ matrix:
|
||||
git:
|
||||
submodules: false # avoid cloning ethereum/tests
|
||||
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.5.linux-amd64.tar.gz | tar -xz
|
||||
- export PATH=`pwd`/go/bin:$PATH
|
||||
- export GOROOT=`pwd`/go
|
||||
- export GOPATH=$HOME/go
|
||||
|
@ -58,13 +58,11 @@ func (abi ABI) Pack(name string, args ...interface{}) ([]byte, error) {
|
||||
return nil, err
|
||||
}
|
||||
return arguments, nil
|
||||
|
||||
}
|
||||
method, exist := abi.Methods[name]
|
||||
if !exist {
|
||||
return nil, fmt.Errorf("method '%s' not found", name)
|
||||
}
|
||||
|
||||
arguments, err := method.Inputs.Pack(args...)
|
||||
if err != nil {
|
||||
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
|
||||
if method, ok := abi.Methods[name]; ok {
|
||||
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)
|
||||
} else if event, ok := abi.Events[name]; ok {
|
||||
|
@ -22,11 +22,10 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math/big"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"reflect"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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" : "slice256", "constant" : false, "inputs" : [ { "name" : "inputs", "type" : "uint256[2]" } ] },
|
||||
{ "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) {
|
||||
Uint256, _ := NewType("uint256")
|
||||
Uint256, _ := NewType("uint256", nil)
|
||||
exp := ABI{
|
||||
Methods: map[string]Method{
|
||||
"balance": {
|
||||
@ -177,7 +179,7 @@ func TestTestSlice(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}
|
||||
exp := "foo(string,string)"
|
||||
if m.Sig() != exp {
|
||||
@ -189,12 +191,31 @@ func TestMethodSignature(t *testing.T) {
|
||||
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}
|
||||
exp = "foo(uint256)"
|
||||
if m.Sig() != exp {
|
||||
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) {
|
||||
@ -564,11 +585,13 @@ func TestBareEvents(t *testing.T) {
|
||||
const definition = `[
|
||||
{ "type" : "event", "name" : "balance" },
|
||||
{ "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")
|
||||
arg1, _ := NewType("address")
|
||||
arg0, _ := NewType("uint256", nil)
|
||||
arg1, _ := NewType("address", nil)
|
||||
tuple, _ := NewType("tuple", []ArgumentMarshaling{{Name: "a", Type: "uint256"}})
|
||||
|
||||
expectedEvents := map[string]struct {
|
||||
Anonymous bool
|
||||
@ -580,6 +603,10 @@ func TestBareEvents(t *testing.T) {
|
||||
{Name: "arg0", Type: arg0, Indexed: false},
|
||||
{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))
|
||||
@ -646,28 +673,24 @@ func TestUnpackEvent(t *testing.T) {
|
||||
}
|
||||
|
||||
type ReceivedEvent struct {
|
||||
Address common.Address
|
||||
Amount *big.Int
|
||||
Memo []byte
|
||||
Sender common.Address
|
||||
Amount *big.Int
|
||||
Memo []byte
|
||||
}
|
||||
var ev ReceivedEvent
|
||||
|
||||
err = abi.Unpack(&ev, "received", data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Logf("len(data): %d; received event: %+v", len(data), ev)
|
||||
}
|
||||
|
||||
type ReceivedAddrEvent struct {
|
||||
Address common.Address
|
||||
Sender common.Address
|
||||
}
|
||||
var receivedAddrEv ReceivedAddrEvent
|
||||
err = abi.Unpack(&receivedAddrEv, "receivedAddr", data)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
} else {
|
||||
t.Logf("len(data): %d; received event: %+v", len(data), receivedAddrEv)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,24 +33,27 @@ type Argument struct {
|
||||
|
||||
type Arguments []Argument
|
||||
|
||||
type ArgumentMarshaling struct {
|
||||
Name string
|
||||
Type string
|
||||
Components []ArgumentMarshaling
|
||||
Indexed bool
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler interface
|
||||
func (argument *Argument) UnmarshalJSON(data []byte) error {
|
||||
var extarg struct {
|
||||
Name string
|
||||
Type string
|
||||
Indexed bool
|
||||
}
|
||||
err := json.Unmarshal(data, &extarg)
|
||||
var arg ArgumentMarshaling
|
||||
err := json.Unmarshal(data, &arg)
|
||||
if err != nil {
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
argument.Name = extarg.Name
|
||||
argument.Indexed = extarg.Indexed
|
||||
argument.Name = arg.Name
|
||||
argument.Indexed = arg.Indexed
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -85,7 +88,6 @@ func (arguments Arguments) isTuple() bool {
|
||||
|
||||
// Unpack performs the operation hexdata -> Go format
|
||||
func (arguments Arguments) Unpack(v interface{}, data []byte) error {
|
||||
|
||||
// make sure the passed value is arguments pointer
|
||||
if reflect.Ptr != reflect.ValueOf(v).Kind() {
|
||||
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() {
|
||||
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 (
|
||||
value = reflect.ValueOf(v).Elem()
|
||||
typ = value.Type()
|
||||
kind = value.Kind()
|
||||
)
|
||||
|
||||
if err := requireUnpackKind(value, typ, kind, arguments); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// If the interface is a struct, get of abi->struct_field mapping
|
||||
|
||||
var abi2struct map[string]string
|
||||
if kind == reflect.Struct {
|
||||
var err error
|
||||
abi2struct, err = mapAbiToStructFields(arguments, value)
|
||||
var (
|
||||
argNames []string
|
||||
err error
|
||||
)
|
||||
for _, arg := range arguments.NonIndexed() {
|
||||
argNames = append(argNames, arg.Name)
|
||||
}
|
||||
abi2struct, err = mapArgNamesToStructFields(argNames, value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i, arg := range arguments.NonIndexed() {
|
||||
|
||||
reflectValue := reflect.ValueOf(marshalledValues[i])
|
||||
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
if structField, ok := abi2struct[arg.Name]; ok {
|
||||
if err := set(value.FieldByName(structField), reflectValue, arg); err != nil {
|
||||
return err
|
||||
}
|
||||
field := value.FieldByName(abi2struct[arg.Name])
|
||||
if !field.IsValid() {
|
||||
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:
|
||||
if value.Len() < i {
|
||||
return fmt.Errorf("abi: insufficient number of arguments for unpack, want %d, got %d", len(arguments), value.Len())
|
||||
}
|
||||
v := value.Index(i)
|
||||
if err := requireAssignable(v, reflectValue); err != nil {
|
||||
if err := requireAssignable(v, reflect.ValueOf(marshalledValues[i])); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := set(v.Elem(), reflectValue, arg); err != nil {
|
||||
if err := unpack(&arg.Type, v.Addr().Interface(), marshalledValues[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
@ -150,48 +234,7 @@ func (arguments Arguments) unpackTuple(v interface{}, marshalledValues []interfa
|
||||
}
|
||||
}
|
||||
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,
|
||||
@ -202,7 +245,7 @@ func (arguments Arguments) UnpackValues(data []byte) ([]interface{}, error) {
|
||||
virtualArgs := 0
|
||||
for index, arg := range arguments.NonIndexed() {
|
||||
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
|
||||
// just like uint256,uint256,uint256.
|
||||
// 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.
|
||||
// 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 {
|
||||
return nil, err
|
||||
@ -243,7 +290,7 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
||||
// input offset is the bytes offset for packed output
|
||||
inputOffset := 0
|
||||
for _, abiArg := range abiArgs {
|
||||
inputOffset += getDynamicTypeOffset(abiArg.Type)
|
||||
inputOffset += getTypeSize(abiArg.Type)
|
||||
}
|
||||
var ret []byte
|
||||
for i, a := range args {
|
||||
@ -272,14 +319,13 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// capitalise makes the first character of a string upper case, also removing any
|
||||
// prefixing underscores from the variable names.
|
||||
func capitalise(input string) string {
|
||||
for len(input) > 0 && input[0] == '_' {
|
||||
input = input[1:]
|
||||
// ToCamelCase converts an under-score string to a camel-case string
|
||||
func ToCamelCase(input string) string {
|
||||
parts := strings.Split(input, "_")
|
||||
for i, s := range parts {
|
||||
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, "")
|
||||
}
|
||||
|
@ -36,10 +36,10 @@ type SignerFn func(types.Signer, common.Address, *types.Transaction) (*types.Tra
|
||||
|
||||
// CallOpts is the collection of options to fine tune a contract call request.
|
||||
type CallOpts struct {
|
||||
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
|
||||
|
||||
Context context.Context // Network context to support cancellation and timeouts (nil = no timeout)
|
||||
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
|
||||
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)
|
||||
}
|
||||
|
||||
// TransactOpts is the collection of authorization data required to create a
|
||||
@ -148,10 +148,10 @@ func (c *BoundContract) Call(opts *CallOpts, result interface{}, method string,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
output, err = c.caller.CallContract(ctx, msg, nil)
|
||||
output, err = c.caller.CallContract(ctx, msg, opts.BlockNumber)
|
||||
if err == nil && len(output) == 0 {
|
||||
// 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
|
||||
} else if len(code) == 0 {
|
||||
return ErrNoCode
|
||||
|
64
accounts/abi/bind/base_test.go
Normal file
64
accounts/abi/bind/base_test.go
Normal 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")
|
||||
}
|
||||
}
|
@ -381,54 +381,23 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
|
||||
// methodNormalizer is a name transformer that modifies Solidity method names to
|
||||
// conform to target language naming concentions.
|
||||
var methodNormalizer = map[Lang]func(string) string{
|
||||
LangGo: capitalise,
|
||||
LangGo: abi.ToCamelCase,
|
||||
LangJava: decapitalise,
|
||||
}
|
||||
|
||||
// capitalise makes a camel-case string which starts with an upper case character.
|
||||
func capitalise(input string) string {
|
||||
for len(input) > 0 && input[0] == '_' {
|
||||
input = input[1:]
|
||||
}
|
||||
if len(input) == 0 {
|
||||
return ""
|
||||
}
|
||||
return toCamelCase(strings.ToUpper(input[:1]) + input[1:])
|
||||
return abi.ToCamelCase(input)
|
||||
}
|
||||
|
||||
// decapitalise makes a camel-case string which starts with a lower case character.
|
||||
func decapitalise(input string) string {
|
||||
for len(input) > 0 && input[0] == '_' {
|
||||
input = input[1:]
|
||||
}
|
||||
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
|
||||
func toCamelCase(input string) string {
|
||||
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
|
||||
goForm := abi.ToCamelCase(input)
|
||||
return strings.ToLower(goForm[:1]) + goForm[1:]
|
||||
}
|
||||
|
||||
// structured checks whether a list of ABI data types has enough information to
|
||||
|
@ -36,12 +36,12 @@ type Event struct {
|
||||
func (e Event) String() string {
|
||||
inputs := make([]string, len(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 {
|
||||
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
|
||||
|
@ -87,12 +87,12 @@ func TestEventId(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
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" : "Balance", "inputs": [{ "name" : "in", "type": "uint256" }] },
|
||||
{ "type" : "event", "name" : "Check", "inputs": [{ "name" : "t", "type": "address" }, { "name": "b", "type": "uint256" }] }
|
||||
]`,
|
||||
expectations: map[string]common.Hash{
|
||||
"balance": crypto.Keccak256Hash([]byte("balance(uint256)")),
|
||||
"check": crypto.Keccak256Hash([]byte("check(address,uint256)")),
|
||||
"Balance": crypto.Keccak256Hash([]byte("Balance(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.
|
||||
func TestEventMultiValueWithArrayUnpack(t *testing.T) {
|
||||
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": false, "name":"value1", "type":"uint8[2]"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`
|
||||
|
@ -56,14 +56,14 @@ func (method Method) Sig() string {
|
||||
func (method Method) String() string {
|
||||
inputs := make([]string, len(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))
|
||||
for i, output := range method.Outputs {
|
||||
outputs[i] = output.Type.String()
|
||||
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 := ""
|
||||
if method.Const {
|
||||
|
61
accounts/abi/method_test.go
Normal file
61
accounts/abi/method_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
@ -29,303 +29,356 @@ import (
|
||||
|
||||
func TestPack(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
typ string
|
||||
|
||||
input interface{}
|
||||
output []byte
|
||||
typ string
|
||||
components []ArgumentMarshaling
|
||||
input interface{}
|
||||
output []byte
|
||||
}{
|
||||
{
|
||||
"uint8",
|
||||
nil,
|
||||
uint8(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"uint8[]",
|
||||
nil,
|
||||
[]uint8{1, 2},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"uint16",
|
||||
nil,
|
||||
uint16(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"uint16[]",
|
||||
nil,
|
||||
[]uint16{1, 2},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"uint32",
|
||||
nil,
|
||||
uint32(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"uint32[]",
|
||||
nil,
|
||||
[]uint32{1, 2},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"uint64",
|
||||
nil,
|
||||
uint64(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"uint64[]",
|
||||
nil,
|
||||
[]uint64{1, 2},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"uint256",
|
||||
nil,
|
||||
big.NewInt(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"uint256[]",
|
||||
nil,
|
||||
[]*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int8",
|
||||
nil,
|
||||
int8(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int8[]",
|
||||
nil,
|
||||
[]int8{1, 2},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int16",
|
||||
nil,
|
||||
int16(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int16[]",
|
||||
nil,
|
||||
[]int16{1, 2},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int32",
|
||||
nil,
|
||||
int32(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int32[]",
|
||||
nil,
|
||||
[]int32{1, 2},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int64",
|
||||
nil,
|
||||
int64(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int64[]",
|
||||
nil,
|
||||
[]int64{1, 2},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int256",
|
||||
nil,
|
||||
big.NewInt(2),
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"int256[]",
|
||||
nil,
|
||||
[]*big.Int{big.NewInt(1), big.NewInt(2)},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002"),
|
||||
},
|
||||
{
|
||||
"bytes1",
|
||||
nil,
|
||||
[1]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes2",
|
||||
nil,
|
||||
[2]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes3",
|
||||
nil,
|
||||
[3]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes4",
|
||||
nil,
|
||||
[4]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes5",
|
||||
nil,
|
||||
[5]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes6",
|
||||
nil,
|
||||
[6]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes7",
|
||||
nil,
|
||||
[7]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes8",
|
||||
nil,
|
||||
[8]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes9",
|
||||
nil,
|
||||
[9]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes10",
|
||||
nil,
|
||||
[10]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes11",
|
||||
nil,
|
||||
[11]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes12",
|
||||
nil,
|
||||
[12]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes13",
|
||||
nil,
|
||||
[13]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes14",
|
||||
nil,
|
||||
[14]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes15",
|
||||
nil,
|
||||
[15]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes16",
|
||||
nil,
|
||||
[16]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes17",
|
||||
nil,
|
||||
[17]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes18",
|
||||
nil,
|
||||
[18]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes19",
|
||||
nil,
|
||||
[19]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes20",
|
||||
nil,
|
||||
[20]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes21",
|
||||
nil,
|
||||
[21]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes22",
|
||||
nil,
|
||||
[22]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes23",
|
||||
nil,
|
||||
[23]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes24",
|
||||
[24]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes24",
|
||||
nil,
|
||||
[24]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes25",
|
||||
nil,
|
||||
[25]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes26",
|
||||
nil,
|
||||
[26]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes27",
|
||||
nil,
|
||||
[27]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes28",
|
||||
nil,
|
||||
[28]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes29",
|
||||
nil,
|
||||
[29]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes30",
|
||||
nil,
|
||||
[30]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes31",
|
||||
nil,
|
||||
[31]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes32",
|
||||
nil,
|
||||
[32]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"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}}},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000000b000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000d000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000000f000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000110000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001300000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000015000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000170000000000000000000000000000000000000000000000000000000000000018"),
|
||||
},
|
||||
{
|
||||
"address[]",
|
||||
nil,
|
||||
[]common.Address{{1}, {2}},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000200000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"bytes32[]",
|
||||
nil,
|
||||
[]common.Hash{{1}, {2}},
|
||||
common.Hex2Bytes("000000000000000000000000000000000000000000000000000000000000000201000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"function",
|
||||
nil,
|
||||
[24]byte{1},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"string",
|
||||
nil,
|
||||
"foobar",
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000006666f6f6261720000000000000000000000000000000000000000000000000000"),
|
||||
},
|
||||
{
|
||||
"string[]",
|
||||
nil,
|
||||
[]string{"hello", "foobar"},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||
@ -337,6 +390,7 @@ func TestPack(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"string[2]",
|
||||
nil,
|
||||
[]string{"hello", "foobar"},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
|
||||
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
|
||||
@ -347,6 +401,7 @@ func TestPack(t *testing.T) {
|
||||
},
|
||||
{
|
||||
"bytes32[][]",
|
||||
nil,
|
||||
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||
@ -362,6 +417,7 @@ func TestPack(t *testing.T) {
|
||||
|
||||
{
|
||||
"bytes32[][2]",
|
||||
nil,
|
||||
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
||||
@ -376,6 +432,7 @@ func TestPack(t *testing.T) {
|
||||
|
||||
{
|
||||
"bytes32[3][2]",
|
||||
nil,
|
||||
[][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||
@ -384,12 +441,182 @@ func TestPack(t *testing.T) {
|
||||
"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
|
||||
}{
|
||||
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)
|
||||
typ, err := NewType(test.typ, test.components)
|
||||
if err != nil {
|
||||
t.Fatalf("%v failed. Unexpected parse error: %v", i, err)
|
||||
}
|
||||
|
||||
output, err := typ.pack(reflect.ValueOf(test.input))
|
||||
if err != nil {
|
||||
t.Fatalf("%v failed. Unexpected pack error: %v", i, err)
|
||||
@ -466,6 +693,59 @@ func TestMethodPack(t *testing.T) {
|
||||
if !bytes.Equal(packed, sig) {
|
||||
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) {
|
||||
|
@ -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
|
||||
// strict ruleset as bare `reflect` does.
|
||||
func set(dst, src reflect.Value, output Argument) error {
|
||||
dstType := dst.Type()
|
||||
srcType := src.Type()
|
||||
func set(dst, src reflect.Value) error {
|
||||
dstType, srcType := dst.Type(), src.Type()
|
||||
switch {
|
||||
case dstType.AssignableTo(srcType):
|
||||
dst.Set(src)
|
||||
case dstType.Kind() == reflect.Interface:
|
||||
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)
|
||||
case dstType.Kind() == reflect.Ptr:
|
||||
return set(dst.Elem(), src, output)
|
||||
case dstType.Kind() == reflect.Slice && srcType.Kind() == reflect.Slice:
|
||||
return setSlice(dst, src)
|
||||
default:
|
||||
return fmt.Errorf("abi: cannot unmarshal %v in to %v", src.Type(), dst.Type())
|
||||
}
|
||||
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.
|
||||
func requireAssignable(dst, src reflect.Value) error {
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
// and this field name exists in the arguments, pair them together.
|
||||
// second round: for each argument field that has not been already linked,
|
||||
// and this field name exists in the given argument name list, pair them together.
|
||||
// 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
|
||||
// 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()
|
||||
|
||||
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]) {
|
||||
continue
|
||||
}
|
||||
|
||||
// skip fields that have no abi:"" tag.
|
||||
var ok bool
|
||||
var tagName string
|
||||
if tagName, ok = typ.Field(i).Tag.Lookup("abi"); !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
// check if tag is empty.
|
||||
if tagName == "" {
|
||||
return nil, fmt.Errorf("struct: abi tag in '%s' is empty", structFieldName)
|
||||
}
|
||||
|
||||
// check which argument field matches with the abi tag.
|
||||
found := false
|
||||
for _, abiField := range args.NonIndexed() {
|
||||
if abiField.Name == tagName {
|
||||
if abi2struct[abiField.Name] != "" {
|
||||
for _, arg := range argNames {
|
||||
if arg == tagName {
|
||||
if abi2struct[arg] != "" {
|
||||
return nil, fmt.Errorf("struct: abi tag in '%s' already mapped", structFieldName)
|
||||
}
|
||||
// pair them
|
||||
abi2struct[abiField.Name] = structFieldName
|
||||
struct2abi[structFieldName] = abiField.Name
|
||||
abi2struct[arg] = structFieldName
|
||||
struct2abi[structFieldName] = arg
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
// check if this tag has been mapped.
|
||||
if !found {
|
||||
return nil, fmt.Errorf("struct: abi tag '%s' defined but not found in abi", tagName)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// second round ~~~
|
||||
for _, arg := range args {
|
||||
for _, argName := range argNames {
|
||||
|
||||
abiFieldName := arg.Name
|
||||
structFieldName := capitalise(abiFieldName)
|
||||
structFieldName := ToCamelCase(argName)
|
||||
|
||||
if structFieldName == "" {
|
||||
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:
|
||||
// abi: [ { "name": "value" } ]
|
||||
// struct { Value *big.Int , Value1 *big.Int `abi:"value"`}
|
||||
if abi2struct[abiFieldName] != "" {
|
||||
if abi2struct[abiFieldName] != structFieldName &&
|
||||
if abi2struct[argName] != "" {
|
||||
if abi2struct[argName] != structFieldName &&
|
||||
struct2abi[structFieldName] == "" &&
|
||||
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
|
||||
}
|
||||
@ -197,16 +205,14 @@ func mapAbiToStructFields(args Arguments, value reflect.Value) (map[string]strin
|
||||
|
||||
if value.FieldByName(structFieldName).IsValid() {
|
||||
// pair them
|
||||
abi2struct[abiFieldName] = structFieldName
|
||||
struct2abi[structFieldName] = abiFieldName
|
||||
abi2struct[argName] = structFieldName
|
||||
struct2abi[structFieldName] = argName
|
||||
} else {
|
||||
// not paired, but annotate as used, to detect cases like
|
||||
// abi : [ { "name": "value" }, { "name": "_value" } ]
|
||||
// struct { Value *big.Int }
|
||||
struct2abi[structFieldName] = abiFieldName
|
||||
struct2abi[structFieldName] = argName
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return abi2struct, nil
|
||||
}
|
||||
|
191
accounts/abi/reflect_test.go
Normal file
191
accounts/abi/reflect_test.go
Normal 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])
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@
|
||||
package abi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@ -32,6 +33,7 @@ const (
|
||||
StringTy
|
||||
SliceTy
|
||||
ArrayTy
|
||||
TupleTy
|
||||
AddressTy
|
||||
FixedBytesTy
|
||||
BytesTy
|
||||
@ -43,13 +45,16 @@ const (
|
||||
// Type is the reflection of the supported argument type
|
||||
type Type struct {
|
||||
Elem *Type
|
||||
|
||||
Kind reflect.Kind
|
||||
Type reflect.Type
|
||||
Size int
|
||||
T byte // Our own type checking
|
||||
|
||||
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 (
|
||||
@ -58,7 +63,7 @@ var (
|
||||
)
|
||||
|
||||
// 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
|
||||
if strings.Count(t, "[") != strings.Count(t, "]") {
|
||||
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 {
|
||||
i := strings.LastIndex(t, "[")
|
||||
// recursively embed the type
|
||||
embeddedType, err := NewType(t[:i])
|
||||
embeddedType, err := NewType(t[:i], components)
|
||||
if err != nil {
|
||||
return Type{}, err
|
||||
}
|
||||
@ -87,6 +92,9 @@ func NewType(t string) (typ Type, err error) {
|
||||
typ.Kind = reflect.Slice
|
||||
typ.Elem = &embeddedType
|
||||
typ.Type = reflect.SliceOf(embeddedType.Type)
|
||||
if embeddedType.T == TupleTy {
|
||||
typ.stringKind = embeddedType.stringKind + sliced
|
||||
}
|
||||
} else if len(intz) == 1 {
|
||||
// is a array
|
||||
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)
|
||||
}
|
||||
typ.Type = reflect.ArrayOf(typ.Size, embeddedType.Type)
|
||||
if embeddedType.T == TupleTy {
|
||||
typ.stringKind = embeddedType.stringKind + sliced
|
||||
}
|
||||
} else {
|
||||
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.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":
|
||||
typ.Kind = reflect.Array
|
||||
typ.T = FunctionTy
|
||||
@ -178,7 +223,6 @@ func (t Type) String() (out string) {
|
||||
func (t Type) pack(v reflect.Value) ([]byte, error) {
|
||||
// dereference pointer first if it's a pointer
|
||||
v = indirect(v)
|
||||
|
||||
if err := typeCheck(t, v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -196,7 +240,7 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
||||
offset := 0
|
||||
offsetReq := isDynamicType(*t.Elem)
|
||||
if offsetReq {
|
||||
offset = getDynamicTypeOffset(*t.Elem) * v.Len()
|
||||
offset = getTypeSize(*t.Elem) * v.Len()
|
||||
}
|
||||
var tail []byte
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
@ -213,6 +257,45 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
||||
tail = append(tail, val...)
|
||||
}
|
||||
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
|
||||
}
|
||||
@ -225,25 +308,45 @@ func (t Type) requiresLengthPrefix() bool {
|
||||
}
|
||||
|
||||
// isDynamicType returns true if the type is dynamic.
|
||||
// StringTy, BytesTy, and SliceTy(irrespective of slice element type) are dynamic types
|
||||
// ArrayTy is considered dynamic if and only if the Array element is a dynamic type.
|
||||
// This function recursively checks the type for slice and array elements.
|
||||
// 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 {
|
||||
// dynamic types
|
||||
// array is also a dynamic type if the array type is dynamic
|
||||
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))
|
||||
}
|
||||
|
||||
// getDynamicTypeOffset returns the offset for the type.
|
||||
// See `isDynamicType` to know which types are considered dynamic.
|
||||
// If the type t is an array and element type is not a dynamic type, then we consider it a static type and
|
||||
// return 32 * size of array since length prefix is not required.
|
||||
// If t is a dynamic type or element type(for slices and arrays) is dynamic, then we simply return 32 as offset.
|
||||
func getDynamicTypeOffset(t Type) int {
|
||||
// if it is an array and there are no dynamic types
|
||||
// then the array is static type
|
||||
// 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) {
|
||||
return 32 * t.Size
|
||||
// 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
|
||||
}
|
||||
|
@ -32,72 +32,75 @@ type typeWithoutStringer Type
|
||||
// Tests that all allowed types get recognized by the type parser.
|
||||
func TestTypeRegexp(t *testing.T) {
|
||||
tests := []struct {
|
||||
blob string
|
||||
kind Type
|
||||
blob string
|
||||
components []ArgumentMarshaling
|
||||
kind Type
|
||||
}{
|
||||
{"bool", Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}},
|
||||
{"bool[]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool(nil)), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}},
|
||||
{"bool[2]", Type{Size: 2, Kind: reflect.Array, T: ArrayTy, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}},
|
||||
{"bool[2][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}},
|
||||
{"bool[][]", Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}},
|
||||
{"bool[][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}},
|
||||
{"bool[2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}},
|
||||
{"bool[2][][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][][2]bool{}), Elem: &Type{Kind: reflect.Slice, T: SliceTy, Type: reflect.TypeOf([][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][]"}, stringKind: "bool[2][][2]"}},
|
||||
{"bool[2][2][2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][2]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[2]"}, stringKind: "bool[2][2]"}, stringKind: "bool[2][2][2]"}},
|
||||
{"bool[][][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][]"}, stringKind: "bool[][][]"}},
|
||||
{"bool[][2][]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][2][]bool{}), Elem: &Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]bool{}), Elem: &Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]bool{}), Elem: &Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), stringKind: "bool"}, stringKind: "bool[]"}, stringKind: "bool[][2]"}, stringKind: "bool[][2][]"}},
|
||||
{"int8", Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
|
||||
{"int16", 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"}},
|
||||
{"int64", 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"}},
|
||||
{"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[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]"}},
|
||||
{"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[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]"}},
|
||||
{"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[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]"}},
|
||||
{"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[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]"}},
|
||||
{"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[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]"}},
|
||||
{"uint8", 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"}},
|
||||
{"uint32", 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"}},
|
||||
{"uint256", 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[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]"}},
|
||||
{"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[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]"}},
|
||||
{"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[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]"}},
|
||||
{"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[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]"}},
|
||||
{"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[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]"}},
|
||||
{"bytes32", Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
|
||||
{"bytes[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][]byte{}), Elem: &Type{Kind: reflect.Slice, Type: reflect.TypeOf([]byte{}), T: BytesTy, stringKind: "bytes"}, stringKind: "bytes[]"}},
|
||||
{"bytes[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][]byte{}), Elem: &Type{T: BytesTy, Type: reflect.TypeOf([]byte{}), Kind: reflect.Slice, stringKind: "bytes"}, stringKind: "bytes[2]"}},
|
||||
{"bytes32[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([][32]byte{}), Elem: &Type{Kind: reflect.Array, Type: reflect.TypeOf([32]byte{}), T: FixedBytesTy, Size: 32, stringKind: "bytes32"}, stringKind: "bytes32[]"}},
|
||||
{"bytes32[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2][32]byte{}), Elem: &Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}, stringKind: "bytes32[2]"}},
|
||||
{"string", Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}},
|
||||
{"string[]", Type{T: SliceTy, Kind: reflect.Slice, Type: reflect.TypeOf([]string{}), Elem: &Type{Kind: reflect.String, Type: reflect.TypeOf(""), T: StringTy, stringKind: "string"}, stringKind: "string[]"}},
|
||||
{"string[2]", Type{Kind: reflect.Array, T: ArrayTy, Size: 2, Type: reflect.TypeOf([2]string{}), Elem: &Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), stringKind: "string"}, stringKind: "string[2]"}},
|
||||
{"address", Type{Kind: reflect.Array, Type: 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[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]"}},
|
||||
{"bool", nil, Type{Kind: reflect.Bool, T: BoolTy, Type: reflect.TypeOf(bool(false)), 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]", 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][]", 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[][]", 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]", 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]", 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]", 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]", 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[][][]", 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][]", 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", nil, Type{Kind: reflect.Int8, Type: int8T, Size: 8, T: IntTy, stringKind: "int8"}},
|
||||
{"int16", nil, Type{Kind: reflect.Int16, Type: int16T, Size: 16, T: IntTy, stringKind: "int16"}},
|
||||
{"int32", nil, Type{Kind: reflect.Int32, Type: int32T, Size: 32, T: IntTy, stringKind: "int32"}},
|
||||
{"int64", nil, Type{Kind: reflect.Int64, Type: int64T, Size: 64, T: IntTy, stringKind: "int64"}},
|
||||
{"int256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: IntTy, stringKind: "int256"}},
|
||||
{"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]", 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[]", 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]", 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[]", 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]", 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[]", 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]", 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[]", 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]", 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", nil, Type{Kind: reflect.Uint8, Type: uint8T, Size: 8, T: UintTy, stringKind: "uint8"}},
|
||||
{"uint16", nil, Type{Kind: reflect.Uint16, Type: uint16T, Size: 16, T: UintTy, stringKind: "uint16"}},
|
||||
{"uint32", nil, Type{Kind: reflect.Uint32, Type: uint32T, Size: 32, T: UintTy, stringKind: "uint32"}},
|
||||
{"uint64", nil, Type{Kind: reflect.Uint64, Type: uint64T, Size: 64, T: UintTy, stringKind: "uint64"}},
|
||||
{"uint256", nil, Type{Kind: reflect.Ptr, Type: bigT, Size: 256, T: UintTy, stringKind: "uint256"}},
|
||||
{"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]", 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[]", 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]", 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[]", 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]", 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[]", 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]", 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[]", 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]", 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", nil, Type{Kind: reflect.Array, T: FixedBytesTy, Size: 32, Type: reflect.TypeOf([32]byte{}), stringKind: "bytes32"}},
|
||||
{"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]", 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[]", 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]", 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", nil, Type{Kind: reflect.String, T: StringTy, Type: reflect.TypeOf(""), 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]", 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", nil, Type{Kind: reflect.Array, Type: addressT, Size: 20, T: AddressTy, 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]", 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
|
||||
// {"fixed", Type{}},
|
||||
// {"fixed128x128", Type{}},
|
||||
// {"fixed[]", Type{}},
|
||||
// {"fixed[2]", Type{}},
|
||||
// {"fixed128x128[]", Type{}},
|
||||
// {"fixed128x128[2]", Type{}},
|
||||
// {"fixed", nil, Type{}},
|
||||
// {"fixed128x128", nil, Type{}},
|
||||
// {"fixed[]", nil, Type{}},
|
||||
// {"fixed[2]", nil, Type{}},
|
||||
// {"fixed128x128[]", nil, 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 {
|
||||
typ, err := NewType(tt.blob)
|
||||
typ, err := NewType(tt.blob, tt.components)
|
||||
if err != nil {
|
||||
t.Errorf("type %q: failed to parse type string: %v", tt.blob, err)
|
||||
}
|
||||
@ -109,154 +112,170 @@ func TestTypeRegexp(t *testing.T) {
|
||||
|
||||
func TestTypeCheck(t *testing.T) {
|
||||
for i, test := range []struct {
|
||||
typ string
|
||||
input interface{}
|
||||
err string
|
||||
typ string
|
||||
components []ArgumentMarshaling
|
||||
input interface{}
|
||||
err string
|
||||
}{
|
||||
{"uint", big.NewInt(1), "unsupported arg type: uint"},
|
||||
{"int", big.NewInt(1), "unsupported arg type: int"},
|
||||
{"uint256", big.NewInt(1), ""},
|
||||
{"uint256[][3][]", [][3][]*big.Int{{{}}}, ""},
|
||||
{"uint256[][][3]", [3][][]*big.Int{{{}}}, ""},
|
||||
{"uint256[3][][]", [][][3]*big.Int{{{}}}, ""},
|
||||
{"uint256[3][3][3]", [3][3][3]*big.Int{{{}}}, ""},
|
||||
{"uint8[][]", [][]uint8{}, ""},
|
||||
{"int256", big.NewInt(1), ""},
|
||||
{"uint8", uint8(1), ""},
|
||||
{"uint16", uint16(1), ""},
|
||||
{"uint32", uint32(1), ""},
|
||||
{"uint64", uint64(1), ""},
|
||||
{"int8", int8(1), ""},
|
||||
{"int16", int16(1), ""},
|
||||
{"int32", int32(1), ""},
|
||||
{"int64", int64(1), ""},
|
||||
{"uint24", big.NewInt(1), ""},
|
||||
{"uint40", big.NewInt(1), ""},
|
||||
{"uint48", big.NewInt(1), ""},
|
||||
{"uint56", big.NewInt(1), ""},
|
||||
{"uint72", big.NewInt(1), ""},
|
||||
{"uint80", big.NewInt(1), ""},
|
||||
{"uint88", big.NewInt(1), ""},
|
||||
{"uint96", big.NewInt(1), ""},
|
||||
{"uint104", big.NewInt(1), ""},
|
||||
{"uint112", big.NewInt(1), ""},
|
||||
{"uint120", big.NewInt(1), ""},
|
||||
{"uint128", big.NewInt(1), ""},
|
||||
{"uint136", big.NewInt(1), ""},
|
||||
{"uint144", big.NewInt(1), ""},
|
||||
{"uint152", big.NewInt(1), ""},
|
||||
{"uint160", big.NewInt(1), ""},
|
||||
{"uint168", big.NewInt(1), ""},
|
||||
{"uint176", big.NewInt(1), ""},
|
||||
{"uint184", big.NewInt(1), ""},
|
||||
{"uint192", big.NewInt(1), ""},
|
||||
{"uint200", big.NewInt(1), ""},
|
||||
{"uint208", big.NewInt(1), ""},
|
||||
{"uint216", big.NewInt(1), ""},
|
||||
{"uint224", big.NewInt(1), ""},
|
||||
{"uint232", big.NewInt(1), ""},
|
||||
{"uint240", big.NewInt(1), ""},
|
||||
{"uint248", big.NewInt(1), ""},
|
||||
{"int24", big.NewInt(1), ""},
|
||||
{"int40", big.NewInt(1), ""},
|
||||
{"int48", big.NewInt(1), ""},
|
||||
{"int56", big.NewInt(1), ""},
|
||||
{"int72", big.NewInt(1), ""},
|
||||
{"int80", big.NewInt(1), ""},
|
||||
{"int88", big.NewInt(1), ""},
|
||||
{"int96", big.NewInt(1), ""},
|
||||
{"int104", big.NewInt(1), ""},
|
||||
{"int112", big.NewInt(1), ""},
|
||||
{"int120", big.NewInt(1), ""},
|
||||
{"int128", big.NewInt(1), ""},
|
||||
{"int136", big.NewInt(1), ""},
|
||||
{"int144", big.NewInt(1), ""},
|
||||
{"int152", big.NewInt(1), ""},
|
||||
{"int160", big.NewInt(1), ""},
|
||||
{"int168", big.NewInt(1), ""},
|
||||
{"int176", big.NewInt(1), ""},
|
||||
{"int184", big.NewInt(1), ""},
|
||||
{"int192", big.NewInt(1), ""},
|
||||
{"int200", big.NewInt(1), ""},
|
||||
{"int208", big.NewInt(1), ""},
|
||||
{"int216", big.NewInt(1), ""},
|
||||
{"int224", big.NewInt(1), ""},
|
||||
{"int232", big.NewInt(1), ""},
|
||||
{"int240", big.NewInt(1), ""},
|
||||
{"int248", big.NewInt(1), ""},
|
||||
{"uint30", uint8(1), "abi: cannot use uint8 as type ptr as argument"},
|
||||
{"uint8", uint16(1), "abi: cannot use uint16 as type uint8 as argument"},
|
||||
{"uint8", uint32(1), "abi: cannot use uint32 as type uint8 as argument"},
|
||||
{"uint8", uint64(1), "abi: cannot use uint64 as type uint8 as argument"},
|
||||
{"uint8", int8(1), "abi: cannot use int8 as type uint8 as argument"},
|
||||
{"uint8", int16(1), "abi: cannot use int16 as type uint8 as argument"},
|
||||
{"uint8", int32(1), "abi: cannot use int32 as type uint8 as argument"},
|
||||
{"uint8", int64(1), "abi: cannot use int64 as type uint8 as argument"},
|
||||
{"uint16", uint16(1), ""},
|
||||
{"uint16", uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
|
||||
{"uint16[]", []uint16{1, 2, 3}, ""},
|
||||
{"uint16[]", [3]uint16{1, 2, 3}, ""},
|
||||
{"uint16[]", []uint32{1, 2, 3}, "abi: cannot use []uint32 as type [0]uint16 as argument"},
|
||||
{"uint16[3]", [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 as type [3]uint16 as argument"},
|
||||
{"uint16[3]", [4]uint16{1, 2, 3}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
|
||||
{"uint16[3]", []uint16{1, 2, 3}, ""},
|
||||
{"uint16[3]", []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
|
||||
{"address[]", []common.Address{{1}}, ""},
|
||||
{"address[1]", []common.Address{{1}}, ""},
|
||||
{"address[1]", [1]common.Address{{1}}, ""},
|
||||
{"address[2]", [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
|
||||
{"bytes32", [32]byte{}, ""},
|
||||
{"bytes31", [31]byte{}, ""},
|
||||
{"bytes30", [30]byte{}, ""},
|
||||
{"bytes29", [29]byte{}, ""},
|
||||
{"bytes28", [28]byte{}, ""},
|
||||
{"bytes27", [27]byte{}, ""},
|
||||
{"bytes26", [26]byte{}, ""},
|
||||
{"bytes25", [25]byte{}, ""},
|
||||
{"bytes24", [24]byte{}, ""},
|
||||
{"bytes23", [23]byte{}, ""},
|
||||
{"bytes22", [22]byte{}, ""},
|
||||
{"bytes21", [21]byte{}, ""},
|
||||
{"bytes20", [20]byte{}, ""},
|
||||
{"bytes19", [19]byte{}, ""},
|
||||
{"bytes18", [18]byte{}, ""},
|
||||
{"bytes17", [17]byte{}, ""},
|
||||
{"bytes16", [16]byte{}, ""},
|
||||
{"bytes15", [15]byte{}, ""},
|
||||
{"bytes14", [14]byte{}, ""},
|
||||
{"bytes13", [13]byte{}, ""},
|
||||
{"bytes12", [12]byte{}, ""},
|
||||
{"bytes11", [11]byte{}, ""},
|
||||
{"bytes10", [10]byte{}, ""},
|
||||
{"bytes9", [9]byte{}, ""},
|
||||
{"bytes8", [8]byte{}, ""},
|
||||
{"bytes7", [7]byte{}, ""},
|
||||
{"bytes6", [6]byte{}, ""},
|
||||
{"bytes5", [5]byte{}, ""},
|
||||
{"bytes4", [4]byte{}, ""},
|
||||
{"bytes3", [3]byte{}, ""},
|
||||
{"bytes2", [2]byte{}, ""},
|
||||
{"bytes1", [1]byte{}, ""},
|
||||
{"bytes32", [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
|
||||
{"bytes32", common.Hash{1}, ""},
|
||||
{"bytes31", common.Hash{1}, "abi: cannot use common.Hash as type [31]uint8 as argument"},
|
||||
{"bytes31", [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
|
||||
{"bytes", []byte{0, 1}, ""},
|
||||
{"bytes", [2]byte{0, 1}, "abi: cannot use array as type slice as argument"},
|
||||
{"bytes", common.Hash{1}, "abi: cannot use array as type slice as argument"},
|
||||
{"string", "hello world", ""},
|
||||
{"string", string(""), ""},
|
||||
{"string", []byte{}, "abi: cannot use slice as type string as argument"},
|
||||
{"bytes32[]", [][32]byte{{}}, ""},
|
||||
{"function", [24]byte{}, ""},
|
||||
{"bytes20", common.Address{}, ""},
|
||||
{"address", [20]byte{}, ""},
|
||||
{"address", common.Address{}, ""},
|
||||
{"bytes32[]]", "", "invalid arg type in abi"},
|
||||
{"invalidType", "", "unsupported arg type: invalidType"},
|
||||
{"invalidSlice[]", "", "unsupported arg type: invalidSlice"},
|
||||
{"uint", nil, big.NewInt(1), "unsupported arg type: uint"},
|
||||
{"int", nil, big.NewInt(1), "unsupported arg type: int"},
|
||||
{"uint256", nil, big.NewInt(1), ""},
|
||||
{"uint256[][3][]", nil, [][3][]*big.Int{{{}}}, ""},
|
||||
{"uint256[][][3]", nil, [3][][]*big.Int{{{}}}, ""},
|
||||
{"uint256[3][][]", nil, [][][3]*big.Int{{{}}}, ""},
|
||||
{"uint256[3][3][3]", nil, [3][3][3]*big.Int{{{}}}, ""},
|
||||
{"uint8[][]", nil, [][]uint8{}, ""},
|
||||
{"int256", nil, big.NewInt(1), ""},
|
||||
{"uint8", nil, uint8(1), ""},
|
||||
{"uint16", nil, uint16(1), ""},
|
||||
{"uint32", nil, uint32(1), ""},
|
||||
{"uint64", nil, uint64(1), ""},
|
||||
{"int8", nil, int8(1), ""},
|
||||
{"int16", nil, int16(1), ""},
|
||||
{"int32", nil, int32(1), ""},
|
||||
{"int64", nil, int64(1), ""},
|
||||
{"uint24", nil, big.NewInt(1), ""},
|
||||
{"uint40", nil, big.NewInt(1), ""},
|
||||
{"uint48", nil, big.NewInt(1), ""},
|
||||
{"uint56", nil, big.NewInt(1), ""},
|
||||
{"uint72", nil, big.NewInt(1), ""},
|
||||
{"uint80", nil, big.NewInt(1), ""},
|
||||
{"uint88", nil, big.NewInt(1), ""},
|
||||
{"uint96", nil, big.NewInt(1), ""},
|
||||
{"uint104", nil, big.NewInt(1), ""},
|
||||
{"uint112", nil, big.NewInt(1), ""},
|
||||
{"uint120", nil, big.NewInt(1), ""},
|
||||
{"uint128", nil, big.NewInt(1), ""},
|
||||
{"uint136", nil, big.NewInt(1), ""},
|
||||
{"uint144", nil, big.NewInt(1), ""},
|
||||
{"uint152", nil, big.NewInt(1), ""},
|
||||
{"uint160", nil, big.NewInt(1), ""},
|
||||
{"uint168", nil, big.NewInt(1), ""},
|
||||
{"uint176", nil, big.NewInt(1), ""},
|
||||
{"uint184", nil, big.NewInt(1), ""},
|
||||
{"uint192", nil, big.NewInt(1), ""},
|
||||
{"uint200", nil, big.NewInt(1), ""},
|
||||
{"uint208", nil, big.NewInt(1), ""},
|
||||
{"uint216", nil, big.NewInt(1), ""},
|
||||
{"uint224", nil, big.NewInt(1), ""},
|
||||
{"uint232", nil, big.NewInt(1), ""},
|
||||
{"uint240", nil, big.NewInt(1), ""},
|
||||
{"uint248", nil, big.NewInt(1), ""},
|
||||
{"int24", nil, big.NewInt(1), ""},
|
||||
{"int40", nil, big.NewInt(1), ""},
|
||||
{"int48", nil, big.NewInt(1), ""},
|
||||
{"int56", nil, big.NewInt(1), ""},
|
||||
{"int72", nil, big.NewInt(1), ""},
|
||||
{"int80", nil, big.NewInt(1), ""},
|
||||
{"int88", nil, big.NewInt(1), ""},
|
||||
{"int96", nil, big.NewInt(1), ""},
|
||||
{"int104", nil, big.NewInt(1), ""},
|
||||
{"int112", nil, big.NewInt(1), ""},
|
||||
{"int120", nil, big.NewInt(1), ""},
|
||||
{"int128", nil, big.NewInt(1), ""},
|
||||
{"int136", nil, big.NewInt(1), ""},
|
||||
{"int144", nil, big.NewInt(1), ""},
|
||||
{"int152", nil, big.NewInt(1), ""},
|
||||
{"int160", nil, big.NewInt(1), ""},
|
||||
{"int168", nil, big.NewInt(1), ""},
|
||||
{"int176", nil, big.NewInt(1), ""},
|
||||
{"int184", nil, big.NewInt(1), ""},
|
||||
{"int192", nil, big.NewInt(1), ""},
|
||||
{"int200", nil, big.NewInt(1), ""},
|
||||
{"int208", nil, big.NewInt(1), ""},
|
||||
{"int216", nil, big.NewInt(1), ""},
|
||||
{"int224", nil, big.NewInt(1), ""},
|
||||
{"int232", nil, big.NewInt(1), ""},
|
||||
{"int240", nil, big.NewInt(1), ""},
|
||||
{"int248", nil, big.NewInt(1), ""},
|
||||
{"uint30", nil, uint8(1), "abi: cannot use uint8 as type ptr as argument"},
|
||||
{"uint8", nil, uint16(1), "abi: cannot use uint16 as type uint8 as argument"},
|
||||
{"uint8", nil, uint32(1), "abi: cannot use uint32 as type uint8 as argument"},
|
||||
{"uint8", nil, uint64(1), "abi: cannot use uint64 as type uint8 as argument"},
|
||||
{"uint8", nil, int8(1), "abi: cannot use int8 as type uint8 as argument"},
|
||||
{"uint8", nil, int16(1), "abi: cannot use int16 as type uint8 as argument"},
|
||||
{"uint8", nil, int32(1), "abi: cannot use int32 as type uint8 as argument"},
|
||||
{"uint8", nil, int64(1), "abi: cannot use int64 as type uint8 as argument"},
|
||||
{"uint16", nil, uint16(1), ""},
|
||||
{"uint16", nil, uint8(1), "abi: cannot use uint8 as type uint16 as argument"},
|
||||
{"uint16[]", nil, []uint16{1, 2, 3}, ""},
|
||||
{"uint16[]", nil, [3]uint16{1, 2, 3}, ""},
|
||||
{"uint16[]", nil, []uint32{1, 2, 3}, "abi: cannot use []uint32 as type [0]uint16 as argument"},
|
||||
{"uint16[3]", nil, [3]uint32{1, 2, 3}, "abi: cannot use [3]uint32 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]", nil, []uint16{1, 2, 3}, ""},
|
||||
{"uint16[3]", nil, []uint16{1, 2, 3, 4}, "abi: cannot use [4]uint16 as type [3]uint16 as argument"},
|
||||
{"address[]", nil, []common.Address{{1}}, ""},
|
||||
{"address[1]", nil, []common.Address{{1}}, ""},
|
||||
{"address[1]", nil, [1]common.Address{{1}}, ""},
|
||||
{"address[2]", nil, [1]common.Address{{1}}, "abi: cannot use [1]array as type [2]array as argument"},
|
||||
{"bytes32", nil, [32]byte{}, ""},
|
||||
{"bytes31", nil, [31]byte{}, ""},
|
||||
{"bytes30", nil, [30]byte{}, ""},
|
||||
{"bytes29", nil, [29]byte{}, ""},
|
||||
{"bytes28", nil, [28]byte{}, ""},
|
||||
{"bytes27", nil, [27]byte{}, ""},
|
||||
{"bytes26", nil, [26]byte{}, ""},
|
||||
{"bytes25", nil, [25]byte{}, ""},
|
||||
{"bytes24", nil, [24]byte{}, ""},
|
||||
{"bytes23", nil, [23]byte{}, ""},
|
||||
{"bytes22", nil, [22]byte{}, ""},
|
||||
{"bytes21", nil, [21]byte{}, ""},
|
||||
{"bytes20", nil, [20]byte{}, ""},
|
||||
{"bytes19", nil, [19]byte{}, ""},
|
||||
{"bytes18", nil, [18]byte{}, ""},
|
||||
{"bytes17", nil, [17]byte{}, ""},
|
||||
{"bytes16", nil, [16]byte{}, ""},
|
||||
{"bytes15", nil, [15]byte{}, ""},
|
||||
{"bytes14", nil, [14]byte{}, ""},
|
||||
{"bytes13", nil, [13]byte{}, ""},
|
||||
{"bytes12", nil, [12]byte{}, ""},
|
||||
{"bytes11", nil, [11]byte{}, ""},
|
||||
{"bytes10", nil, [10]byte{}, ""},
|
||||
{"bytes9", nil, [9]byte{}, ""},
|
||||
{"bytes8", nil, [8]byte{}, ""},
|
||||
{"bytes7", nil, [7]byte{}, ""},
|
||||
{"bytes6", nil, [6]byte{}, ""},
|
||||
{"bytes5", nil, [5]byte{}, ""},
|
||||
{"bytes4", nil, [4]byte{}, ""},
|
||||
{"bytes3", nil, [3]byte{}, ""},
|
||||
{"bytes2", nil, [2]byte{}, ""},
|
||||
{"bytes1", nil, [1]byte{}, ""},
|
||||
{"bytes32", nil, [33]byte{}, "abi: cannot use [33]uint8 as type [32]uint8 as argument"},
|
||||
{"bytes32", nil, common.Hash{1}, ""},
|
||||
{"bytes31", nil, common.Hash{1}, "abi: cannot use common.Hash as type [31]uint8 as argument"},
|
||||
{"bytes31", nil, [32]byte{}, "abi: cannot use [32]uint8 as type [31]uint8 as argument"},
|
||||
{"bytes", nil, []byte{0, 1}, ""},
|
||||
{"bytes", nil, [2]byte{0, 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", nil, "hello world", ""},
|
||||
{"string", nil, string(""), ""},
|
||||
{"string", nil, []byte{}, "abi: cannot use slice as type string as argument"},
|
||||
{"bytes32[]", nil, [][32]byte{{}}, ""},
|
||||
{"function", nil, [24]byte{}, ""},
|
||||
{"bytes20", nil, common.Address{}, ""},
|
||||
{"address", nil, [20]byte{}, ""},
|
||||
{"address", nil, common.Address{}, ""},
|
||||
{"bytes32[]]", nil, "", "invalid arg type in abi"},
|
||||
{"invalidType", nil, "", "unsupported arg type: invalidType"},
|
||||
{"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 {
|
||||
t.Fatal("unexpected parse error:", err)
|
||||
} else if err != nil && len(test.err) != 0 {
|
||||
|
@ -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
|
||||
func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error) {
|
||||
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.
|
||||
// Slices have just 32 bytes per element (pointing to the contents).
|
||||
elemSize := 32
|
||||
if t.T == ArrayTy {
|
||||
elemSize = getFullElemSize(t.Elem)
|
||||
}
|
||||
elemSize := getTypeSize(*t.Elem)
|
||||
|
||||
for i, j := start, 0; j < size; i, j = i+elemSize, j+1 {
|
||||
|
||||
inter, err := toGoType(i, *t.Elem, output)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -170,6 +155,36 @@ func forEachUnpack(t Type, output []byte, start, size int) (interface{}, error)
|
||||
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
|
||||
// into a go type with accordance with the ABI spec.
|
||||
func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||
@ -178,14 +193,14 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||
}
|
||||
|
||||
var (
|
||||
returnOutput []byte
|
||||
begin, end int
|
||||
err error
|
||||
returnOutput []byte
|
||||
begin, length int
|
||||
err error
|
||||
)
|
||||
|
||||
// if we require a length prefix, find the beginning word and size returned.
|
||||
if t.requiresLengthPrefix() {
|
||||
begin, end, err = lengthPrefixPointsTo(index, output)
|
||||
begin, length, err = lengthPrefixPointsTo(index, output)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -194,12 +209,26 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||
}
|
||||
|
||||
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:
|
||||
return forEachUnpack(t, output, begin, end)
|
||||
return forEachUnpack(t, output[begin:], 0, length)
|
||||
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
|
||||
return string(output[begin : begin+end]), nil
|
||||
return string(output[begin : begin+length]), nil
|
||||
case IntTy, UintTy:
|
||||
return readInteger(t.T, t.Kind, returnOutput), nil
|
||||
case BoolTy:
|
||||
@ -209,7 +238,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
|
||||
case HashTy:
|
||||
return common.BytesToHash(returnOutput), nil
|
||||
case BytesTy:
|
||||
return output[begin : begin+end], nil
|
||||
return output[begin : begin+length], nil
|
||||
case FixedBytesTy:
|
||||
return readFixedBytes(t, returnOutput)
|
||||
case FunctionTy:
|
||||
@ -250,3 +279,17 @@ func lengthPrefixPointsTo(index int, output []byte) (start int, length int, err
|
||||
length = int(lengthBig.Uint64())
|
||||
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
|
||||
}
|
||||
|
@ -173,9 +173,14 @@ var unpackTests = []unpackTest{
|
||||
// multi dimensional, if these pass, all types that don't require length prefix should pass
|
||||
{
|
||||
def: `[{"type": "uint8[][]"}]`,
|
||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000E0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
want: [][]uint8{{1, 2}, {1, 2}},
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint8[][]"}]`,
|
||||
enc: "00000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
||||
want: [][]uint8{{1, 2}, {1, 2, 3}},
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint8[2][2]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
@ -183,7 +188,7 @@ var unpackTests = []unpackTest{
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint8[][2]"}]`,
|
||||
enc: "000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001",
|
||||
want: [2][]uint8{{1}, {1}},
|
||||
},
|
||||
{
|
||||
@ -191,6 +196,11 @@ var unpackTests = []unpackTest{
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
want: [][2]uint8{{1, 2}},
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint8[2][]"}]`,
|
||||
enc: "000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
want: [][2]uint8{{1, 2}, {1, 2}},
|
||||
},
|
||||
{
|
||||
def: `[{"type": "uint16[]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
@ -236,6 +246,26 @@ var unpackTests = []unpackTest{
|
||||
enc: "000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003",
|
||||
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[]"}]`,
|
||||
enc: "0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000002",
|
||||
@ -295,6 +325,53 @@ var unpackTests = []unpackTest{
|
||||
Int2 *big.Int
|
||||
}{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"}]`,
|
||||
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 {
|
||||
Int *big.Int
|
||||
String string
|
||||
@ -462,6 +588,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) {
|
||||
// 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
|
||||
@ -751,6 +939,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) {
|
||||
oomTests := []unpackTest{
|
||||
{
|
||||
|
@ -52,8 +52,8 @@ func (w *keystoreWallet) Status() (string, error) {
|
||||
// is no connection or decryption step necessary to access the list of accounts.
|
||||
func (w *keystoreWallet) Open(passphrase string) error { return nil }
|
||||
|
||||
// Close implements accounts.Wallet, but is a noop for plain wallets since is no
|
||||
// meaningful open operation.
|
||||
// Close implements accounts.Wallet, but is a noop for plain wallets since there
|
||||
// is no meaningful open operation.
|
||||
func (w *keystoreWallet) Close() error { return nil }
|
||||
|
||||
// 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).
|
||||
func (w *keystoreWallet) SignHash(account accounts.Account, hash []byte) ([]byte, error) {
|
||||
// Make sure the requested account is contained within
|
||||
if account.Address != w.account.Address {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
|
||||
if !w.Contains(account) {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
// 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).
|
||||
func (w *keystoreWallet) SignTx(account accounts.Account, tx *types.Transaction, chainID *big.Int) (*types.Transaction, error) {
|
||||
// Make sure the requested account is contained within
|
||||
if account.Address != w.account.Address {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
|
||||
if !w.Contains(account) {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
// 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.
|
||||
func (w *keystoreWallet) SignHashWithPassphrase(account accounts.Account, passphrase string, hash []byte) ([]byte, error) {
|
||||
// Make sure the requested account is contained within
|
||||
if account.Address != w.account.Address {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
|
||||
if !w.Contains(account) {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
// 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.
|
||||
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
|
||||
if account.Address != w.account.Address {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
if account.URL != (accounts.URL{}) && account.URL != w.account.URL {
|
||||
if !w.Contains(account) {
|
||||
return nil, accounts.ErrUnknownAccount
|
||||
}
|
||||
// Account seems valid, request the keystore to sign
|
||||
|
@ -257,7 +257,9 @@ func (w *ledgerDriver) ledgerDerive(derivationPath []uint32) (common.Address, er
|
||||
|
||||
// Decode the hex sting into an Ethereum address and return
|
||||
var address common.Address
|
||||
hex.Decode(address[:], hexstr)
|
||||
if _, err = hex.Decode(address[:], hexstr); err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
return address, nil
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@ environment:
|
||||
install:
|
||||
- git submodule update --init
|
||||
- rmdir C:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.11.2.windows-%GETH_ARCH%.zip
|
||||
- 7z x go1.11.2.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.11.5.windows-%GETH_ARCH%.zip
|
||||
- 7z x go1.11.5.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||
- go version
|
||||
- gcc --version
|
||||
|
||||
|
@ -7,11 +7,18 @@ Canonical.
|
||||
Packages of develop branch commits have suffix -unstable and cannot be installed alongside
|
||||
the stable version. Switching between release streams requires user intervention.
|
||||
|
||||
## Launchpad
|
||||
|
||||
The packages are built and served by launchpad.net. We generate a Debian source package
|
||||
for each distribution and upload it. Their builder picks up the source package, builds it
|
||||
and installs the new version into the PPA repository. Launchpad requires a valid signature
|
||||
by a team member for source package uploads. The signing key is stored in an environment
|
||||
variable which Travis CI makes available to certain builds.
|
||||
by a team member for source package uploads.
|
||||
|
||||
The signing key is stored in an environment variable which Travis CI makes available to
|
||||
certain builds. Since Travis CI doesn't support FTP, SFTP is used to transfer the
|
||||
packages. To set this up yourself, you need to create a Launchpad user and add a GPG key
|
||||
and SSH key to it. Then encode both keys as base64 and configure 'secret' environment
|
||||
variables `PPA_SIGNING_KEY` and `PPA_SSH_KEY` on Travis.
|
||||
|
||||
We want to build go-ethereum with the most recent version of Go, irrespective of the Go
|
||||
version that is available in the main Ubuntu repository. In order to make this possible,
|
||||
@ -27,7 +34,7 @@ Add the gophers PPA and install Go 1.10 and Debian packaging tools:
|
||||
|
||||
$ sudo apt-add-repository ppa:gophers/ubuntu/archive
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install build-essential golang-1.10 devscripts debhelper
|
||||
$ sudo apt-get install build-essential golang-1.10 devscripts debhelper python-bzrlib python-paramiko
|
||||
|
||||
Create the source packages:
|
||||
|
||||
|
67
build/ci.go
67
build/ci.go
@ -441,11 +441,8 @@ func archiveBasename(arch string, archiveVersion string) string {
|
||||
func archiveUpload(archive string, blobstore string, signer string) error {
|
||||
// If signing was requested, generate the signature files
|
||||
if signer != "" {
|
||||
pgpkey, err := base64.StdEncoding.DecodeString(os.Getenv(signer))
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid base64 %s", signer)
|
||||
}
|
||||
if err := build.PGPSignFile(archive, archive+".asc", string(pgpkey)); err != nil {
|
||||
key := getenvBase64(signer)
|
||||
if err := build.PGPSignFile(archive, archive+".asc", string(key)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -488,7 +485,8 @@ func maybeSkipArchive(env build.Environment) {
|
||||
func doDebianSource(cmdline []string) {
|
||||
var (
|
||||
signer = flag.String("signer", "", `Signing key name, also used as package author`)
|
||||
upload = flag.String("upload", "", `Where to upload the source package (usually "ppa:ethereum/ethereum")`)
|
||||
upload = flag.String("upload", "", `Where to upload the source package (usually "ethereum/ethereum")`)
|
||||
sshUser = flag.String("sftp-user", "", `Username for SFTP upload (usually "geth-ci")`)
|
||||
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
|
||||
now = time.Now()
|
||||
)
|
||||
@ -498,11 +496,7 @@ func doDebianSource(cmdline []string) {
|
||||
maybeSkipArchive(env)
|
||||
|
||||
// Import the signing key.
|
||||
if b64key := os.Getenv("PPA_SIGNING_KEY"); b64key != "" {
|
||||
key, err := base64.StdEncoding.DecodeString(b64key)
|
||||
if err != nil {
|
||||
log.Fatal("invalid base64 PPA_SIGNING_KEY")
|
||||
}
|
||||
if key := getenvBase64("PPA_SIGNING_KEY"); len(key) > 0 {
|
||||
gpg := exec.Command("gpg", "--import")
|
||||
gpg.Stdin = bytes.NewReader(key)
|
||||
build.MustRun(gpg)
|
||||
@ -513,22 +507,58 @@ func doDebianSource(cmdline []string) {
|
||||
for _, distro := range debDistros {
|
||||
meta := newDebMetadata(distro, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables)
|
||||
pkgdir := stageDebianSource(*workdir, meta)
|
||||
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc")
|
||||
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc", "-d", "-Zxz")
|
||||
debuild.Dir = pkgdir
|
||||
build.MustRun(debuild)
|
||||
|
||||
changes := fmt.Sprintf("%s_%s_source.changes", meta.Name(), meta.VersionString())
|
||||
changes = filepath.Join(*workdir, changes)
|
||||
var (
|
||||
basename = fmt.Sprintf("%s_%s", meta.Name(), meta.VersionString())
|
||||
source = filepath.Join(*workdir, basename+".tar.xz")
|
||||
dsc = filepath.Join(*workdir, basename+".dsc")
|
||||
changes = filepath.Join(*workdir, basename+"_source.changes")
|
||||
)
|
||||
if *signer != "" {
|
||||
build.MustRunCommand("debsign", changes)
|
||||
}
|
||||
if *upload != "" {
|
||||
build.MustRunCommand("dput", *upload, changes)
|
||||
ppaUpload(*workdir, *upload, *sshUser, []string{source, dsc, changes})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ppaUpload(workdir, ppa, sshUser string, files []string) {
|
||||
p := strings.Split(ppa, "/")
|
||||
if len(p) != 2 {
|
||||
log.Fatal("-upload PPA name must contain single /")
|
||||
}
|
||||
if sshUser == "" {
|
||||
sshUser = p[0]
|
||||
}
|
||||
incomingDir := fmt.Sprintf("~%s/ubuntu/%s", p[0], p[1])
|
||||
// Create the SSH identity file if it doesn't exist.
|
||||
var idfile string
|
||||
if sshkey := getenvBase64("PPA_SSH_KEY"); len(sshkey) > 0 {
|
||||
idfile = filepath.Join(workdir, "sshkey")
|
||||
if _, err := os.Stat(idfile); os.IsNotExist(err) {
|
||||
ioutil.WriteFile(idfile, sshkey, 0600)
|
||||
}
|
||||
}
|
||||
// Upload
|
||||
dest := sshUser + "@ppa.launchpad.net"
|
||||
if err := build.UploadSFTP(idfile, dest, incomingDir, files); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func getenvBase64(variable string) []byte {
|
||||
dec, err := base64.StdEncoding.DecodeString(os.Getenv(variable))
|
||||
if err != nil {
|
||||
log.Fatal("invalid base64 " + variable)
|
||||
}
|
||||
return []byte(dec)
|
||||
}
|
||||
|
||||
func makeWorkdir(wdflag string) string {
|
||||
var err error
|
||||
if wdflag != "" {
|
||||
@ -800,15 +830,10 @@ func doAndroidArchive(cmdline []string) {
|
||||
os.Rename(archive, meta.Package+".aar")
|
||||
if *signer != "" && *deploy != "" {
|
||||
// Import the signing key into the local GPG instance
|
||||
b64key := os.Getenv(*signer)
|
||||
key, err := base64.StdEncoding.DecodeString(b64key)
|
||||
if err != nil {
|
||||
log.Fatalf("invalid base64 %s", *signer)
|
||||
}
|
||||
key := getenvBase64(*signer)
|
||||
gpg := exec.Command("gpg", "--import")
|
||||
gpg.Stdin = bytes.NewReader(key)
|
||||
build.MustRun(gpg)
|
||||
|
||||
keyID, err := build.PGPKeyID(string(key))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -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
|
||||
|
||||
/*
|
||||
|
@ -20,7 +20,6 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
@ -198,7 +197,17 @@ func dumpConfig(ctx *cli.Context) error {
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
@ -38,7 +38,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -121,6 +121,7 @@ var (
|
||||
utils.DeveloperPeriodFlag,
|
||||
utils.TestnetFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.ConstantinopleOverrideFlag,
|
||||
@ -164,7 +165,7 @@ var (
|
||||
utils.MetricsInfluxDBDatabaseFlag,
|
||||
utils.MetricsInfluxDBUsernameFlag,
|
||||
utils.MetricsInfluxDBPasswordFlag,
|
||||
utils.MetricsInfluxDBHostTagFlag,
|
||||
utils.MetricsInfluxDBTagsFlag,
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -26,7 +26,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
// AppHelpTemplate is the test template for the default, global app help topic.
|
||||
@ -74,6 +74,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
utils.NetworkIdFlag,
|
||||
utils.TestnetFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.SyncModeFlag,
|
||||
utils.GCModeFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
@ -229,7 +230,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
utils.MetricsInfluxDBDatabaseFlag,
|
||||
utils.MetricsInfluxDBUsernameFlag,
|
||||
utils.MetricsInfluxDBPasswordFlag,
|
||||
utils.MetricsInfluxDBHostTagFlag,
|
||||
utils.MetricsInfluxDBTagsFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -174,7 +174,11 @@ func (spec *alethGenesisSpec) setPrecompile(address byte, data *alethGenesisSpec
|
||||
if spec.Accounts == nil {
|
||||
spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount)
|
||||
}
|
||||
spec.Accounts[common.UnprefixedAddress(common.BytesToAddress([]byte{address}))].Precompiled = data
|
||||
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) {
|
||||
@ -219,28 +223,29 @@ type parityChainSpec struct {
|
||||
} `json:"engine"`
|
||||
|
||||
Params struct {
|
||||
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
|
||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
|
||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||
ChainID hexutil.Uint64 `json:"chainID"`
|
||||
MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"`
|
||||
MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"`
|
||||
EIP98Transition hexutil.Uint64 `json:"eip98Transition"`
|
||||
EIP150Transition hexutil.Uint64 `json:"eip150Transition"`
|
||||
EIP160Transition hexutil.Uint64 `json:"eip160Transition"`
|
||||
EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"`
|
||||
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"`
|
||||
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
|
||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
|
||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||
ChainID hexutil.Uint64 `json:"chainID"`
|
||||
MaxCodeSize hexutil.Uint64 `json:"maxCodeSize"`
|
||||
MaxCodeSizeTransition hexutil.Uint64 `json:"maxCodeSizeTransition"`
|
||||
EIP98Transition hexutil.Uint64 `json:"eip98Transition"`
|
||||
EIP150Transition hexutil.Uint64 `json:"eip150Transition"`
|
||||
EIP160Transition hexutil.Uint64 `json:"eip160Transition"`
|
||||
EIP161abcTransition hexutil.Uint64 `json:"eip161abcTransition"`
|
||||
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"`
|
||||
|
||||
Genesis struct {
|
||||
@ -343,6 +348,11 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
|
||||
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.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
||||
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
|
||||
@ -437,6 +447,10 @@ func (spec *parityChainSpec) setConstantinople(num *big.Int) {
|
||||
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
|
||||
// Python Ethereum implementation.
|
||||
type pyEthereumGenesisSpec struct {
|
||||
|
@ -608,30 +608,31 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
|
||||
bootPython[i] = "'" + boot + "'"
|
||||
}
|
||||
template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{
|
||||
"Network": network,
|
||||
"NetworkID": conf.Genesis.Config.ChainID,
|
||||
"NetworkTitle": strings.Title(network),
|
||||
"EthstatsPage": config.ethstats,
|
||||
"ExplorerPage": config.explorer,
|
||||
"WalletPage": config.wallet,
|
||||
"FaucetPage": config.faucet,
|
||||
"GethGenesis": network + ".json",
|
||||
"Bootnodes": conf.bootnodes,
|
||||
"BootnodesFlat": strings.Join(conf.bootnodes, ","),
|
||||
"Ethstats": statsLogin,
|
||||
"Ethash": conf.Genesis.Config.Ethash != nil,
|
||||
"CppGenesis": network + "-cpp.json",
|
||||
"CppBootnodes": strings.Join(bootCpp, " "),
|
||||
"HarmonyGenesis": network + "-harmony.json",
|
||||
"HarmonyBootnodes": strings.Join(bootHarmony, " "),
|
||||
"ParityGenesis": network + "-parity.json",
|
||||
"PythonGenesis": network + "-python.json",
|
||||
"PythonBootnodes": strings.Join(bootPython, ","),
|
||||
"Homestead": conf.Genesis.Config.HomesteadBlock,
|
||||
"Tangerine": conf.Genesis.Config.EIP150Block,
|
||||
"Spurious": conf.Genesis.Config.EIP155Block,
|
||||
"Byzantium": conf.Genesis.Config.ByzantiumBlock,
|
||||
"Constantinople": conf.Genesis.Config.ConstantinopleBlock,
|
||||
"Network": network,
|
||||
"NetworkID": conf.Genesis.Config.ChainID,
|
||||
"NetworkTitle": strings.Title(network),
|
||||
"EthstatsPage": config.ethstats,
|
||||
"ExplorerPage": config.explorer,
|
||||
"WalletPage": config.wallet,
|
||||
"FaucetPage": config.faucet,
|
||||
"GethGenesis": network + ".json",
|
||||
"Bootnodes": conf.bootnodes,
|
||||
"BootnodesFlat": strings.Join(conf.bootnodes, ","),
|
||||
"Ethstats": statsLogin,
|
||||
"Ethash": conf.Genesis.Config.Ethash != nil,
|
||||
"CppGenesis": network + "-cpp.json",
|
||||
"CppBootnodes": strings.Join(bootCpp, " "),
|
||||
"HarmonyGenesis": network + "-harmony.json",
|
||||
"HarmonyBootnodes": strings.Join(bootHarmony, " "),
|
||||
"ParityGenesis": network + "-parity.json",
|
||||
"PythonGenesis": network + "-python.json",
|
||||
"PythonBootnodes": strings.Join(bootPython, ","),
|
||||
"Homestead": conf.Genesis.Config.HomesteadBlock,
|
||||
"Tangerine": conf.Genesis.Config.EIP150Block,
|
||||
"Spurious": conf.Genesis.Config.EIP155Block,
|
||||
"Byzantium": conf.Genesis.Config.ByzantiumBlock,
|
||||
"Constantinople": conf.Genesis.Config.ConstantinopleBlock,
|
||||
"ConstantinopleFix": conf.Genesis.Config.PetersburgBlock,
|
||||
})
|
||||
files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()
|
||||
|
||||
|
@ -222,10 +222,18 @@ func (w *wizard) manageGenesis() {
|
||||
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)
|
||||
if w.conf.Genesis.Config.PetersburgBlock == nil {
|
||||
w.conf.Genesis.Config.PetersburgBlock = 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.PetersburgBlock)
|
||||
w.conf.Genesis.Config.PetersburgBlock = w.readDefaultBigInt(w.conf.Genesis.Config.PetersburgBlock)
|
||||
|
||||
out, _ := json.MarshalIndent(w.conf.Genesis.Config, "", " ")
|
||||
fmt.Printf("Chain configuration updated:\n\n%s\n", out)
|
||||
|
||||
w.conf.flush()
|
||||
|
||||
case "2":
|
||||
// Save whatever genesis configuration we currently have
|
||||
fmt.Println()
|
||||
|
@ -33,11 +33,11 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"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/swarm/api"
|
||||
swarmapi "github.com/ethereum/go-ethereum/swarm/api/client"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -598,7 +598,7 @@ func TestKeypairSanity(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
hasher := sha3.NewKeccak256()
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher.Write(salt)
|
||||
shared, err := hex.DecodeString(sharedSecret)
|
||||
if err != nil {
|
||||
|
@ -17,61 +17,8 @@
|
||||
package main
|
||||
|
||||
var SwarmBootnodes = []string{
|
||||
// Foundation Swarm Gateway Cluster
|
||||
"enode://e5c6f9215c919a5450a7b8c14c22535607b69f2c8e1e7f6f430cb25d7a2c27cd1df4c4f18ad7c1d7e5162e271ffcd3f20b1a1467fb6e790e7d727f3b2193de97@52.232.7.187:30399",
|
||||
"enode://9b2fe07e69ccc7db5fef15793dab7d7d2e697ed92132d6e9548218e68a34613a8671ad03a6658d862b468ed693cae8a0f8f8d37274e4a657ffb59ca84676e45b@52.232.7.187:30400",
|
||||
"enode://76c1059162c93ef9df0f01097c824d17c492634df211ef4c806935b349082233b63b90c23970254b3b7138d630400f7cf9b71e80355a446a8b733296cb04169a@52.232.7.187:30401",
|
||||
"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",
|
||||
// EF Swarm Bootnode - AWS - eu-central-1
|
||||
"enode://4c113504601930bf2000c29bcd98d1716b6167749f58bad703bae338332fe93cc9d9204f08afb44100dc7bea479205f5d162df579f9a8f76f8b402d339709023@3.122.203.99:30301",
|
||||
// EF Swarm Bootnode - AWS - us-west-2
|
||||
"enode://89f2ede3371bff1ad9f2088f2012984e280287a4e2b68007c2a6ad994909c51886b4a8e9e2ecc97f9910aca538398e0a5804b0ee80a187fde1ba4f32626322ba@52.35.212.179:30301",
|
||||
}
|
||||
|
@ -79,8 +79,10 @@ const (
|
||||
SWARM_ENV_STORE_PATH = "SWARM_STORE_PATH"
|
||||
SWARM_ENV_STORE_CAPACITY = "SWARM_STORE_CAPACITY"
|
||||
SWARM_ENV_STORE_CACHE_CAPACITY = "SWARM_STORE_CACHE_CAPACITY"
|
||||
SWARM_ENV_BOOTNODE_MODE = "SWARM_BOOTNODE_MODE"
|
||||
SWARM_ACCESS_PASSWORD = "SWARM_ACCESS_PASSWORD"
|
||||
SWARM_AUTO_DEFAULTPATH = "SWARM_AUTO_DEFAULTPATH"
|
||||
SWARM_GLOBALSTORE_API = "SWARM_GLOBALSTORE_API"
|
||||
GETH_ENV_DATADIR = "GETH_DATADIR"
|
||||
)
|
||||
|
||||
@ -164,10 +166,9 @@ func configFileOverride(config *bzzapi.Config, ctx *cli.Context) (*bzzapi.Config
|
||||
return config, err
|
||||
}
|
||||
|
||||
//override 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
|
||||
// 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
|
||||
func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Config {
|
||||
|
||||
if keyid := ctx.GlobalString(SwarmAccountFlag.Name); keyid != "" {
|
||||
currentConfig.BzzAccount = keyid
|
||||
}
|
||||
@ -258,14 +259,21 @@ func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Con
|
||||
currentConfig.LocalStoreParams.CacheCapacity = storeCacheCapacity
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(SwarmBootnodeModeFlag.Name) {
|
||||
currentConfig.BootnodeMode = ctx.GlobalBool(SwarmBootnodeModeFlag.Name)
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(SwarmGlobalStoreAPIFlag.Name) {
|
||||
currentConfig.GlobalStoreAPI = ctx.GlobalString(SwarmGlobalStoreAPIFlag.Name)
|
||||
}
|
||||
|
||||
return currentConfig
|
||||
|
||||
}
|
||||
|
||||
//override the current config with whatver is provided in environment variables
|
||||
//most values are not allowed a zero value (empty string), if not otherwise noted
|
||||
// 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
|
||||
func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
|
||||
|
||||
if keyid := os.Getenv(SWARM_ENV_ACCOUNT); keyid != "" {
|
||||
currentConfig.BzzAccount = keyid
|
||||
}
|
||||
@ -364,6 +372,18 @@ func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
|
||||
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
|
||||
}
|
||||
|
||||
if api := os.Getenv(SWARM_GLOBALSTORE_API); api != "" {
|
||||
currentConfig.GlobalStoreAPI = api
|
||||
}
|
||||
|
||||
return currentConfig
|
||||
}
|
||||
|
||||
|
59
cmd/swarm/explore.go
Normal file
59
cmd/swarm/explore.go
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2019 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/>.
|
||||
|
||||
// Command bzzhash computes a swarm tree hash.
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var hashesCommand = cli.Command{
|
||||
Action: hashes,
|
||||
CustomHelpTemplate: helpTemplate,
|
||||
Name: "hashes",
|
||||
Usage: "print all hashes of a file to STDOUT",
|
||||
ArgsUsage: "<file>",
|
||||
Description: "Prints all hashes of a file to STDOUT",
|
||||
}
|
||||
|
||||
func hashes(ctx *cli.Context) {
|
||||
args := ctx.Args()
|
||||
if len(args) < 1 {
|
||||
utils.Fatalf("Usage: swarm hashes <file name>")
|
||||
}
|
||||
f, err := os.Open(args[0])
|
||||
if err != nil {
|
||||
utils.Fatalf("Error opening file " + args[1])
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
fileStore := storage.NewFileStore(&storage.FakeChunkStore{}, storage.NewFileStoreParams())
|
||||
refs, err := fileStore.GetAllReferences(context.TODO(), f, false)
|
||||
if err != nil {
|
||||
utils.Fatalf("%v\n", err)
|
||||
} else {
|
||||
for _, r := range refs {
|
||||
fmt.Println(r.String())
|
||||
}
|
||||
}
|
||||
}
|
@ -156,6 +156,10 @@ var (
|
||||
Name: "compressed",
|
||||
Usage: "Prints encryption keys in compressed form",
|
||||
}
|
||||
SwarmBootnodeModeFlag = cli.BoolFlag{
|
||||
Name: "bootnode-mode",
|
||||
Usage: "Run Swarm in Bootnode mode",
|
||||
}
|
||||
SwarmFeedNameFlag = cli.StringFlag{
|
||||
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",
|
||||
@ -164,10 +168,6 @@ var (
|
||||
Name: "topic",
|
||||
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{
|
||||
Name: "manifest",
|
||||
Usage: "Refers to the feed through a manifest",
|
||||
@ -176,4 +176,9 @@ var (
|
||||
Name: "user",
|
||||
Usage: "Indicates the user who updates the feed",
|
||||
}
|
||||
SwarmGlobalStoreAPIFlag = cli.StringFlag{
|
||||
Name: "globalstore-api",
|
||||
Usage: "URL of the Global Store API provider (only for testing)",
|
||||
EnvVar: SWARM_GLOBALSTORE_API,
|
||||
}
|
||||
)
|
||||
|
100
cmd/swarm/global-store/global_store.go
Normal file
100
cmd/swarm/global-store/global_store.go
Normal file
@ -0,0 +1,100 @@
|
||||
// Copyright 2019 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 (
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/mock"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/mock/db"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/mock/mem"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
// startHTTP starts a global store with HTTP RPC server.
|
||||
// It is used for "http" cli command.
|
||||
func startHTTP(ctx *cli.Context) (err error) {
|
||||
server, cleanup, err := newServer(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
listener, err := net.Listen("tcp", ctx.String("addr"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Info("http", "address", listener.Addr().String())
|
||||
|
||||
return http.Serve(listener, server)
|
||||
}
|
||||
|
||||
// startWS starts a global store with WebSocket RPC server.
|
||||
// It is used for "websocket" cli command.
|
||||
func startWS(ctx *cli.Context) (err error) {
|
||||
server, cleanup, err := newServer(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer cleanup()
|
||||
|
||||
listener, err := net.Listen("tcp", ctx.String("addr"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
origins := ctx.StringSlice("origins")
|
||||
log.Info("websocket", "address", listener.Addr().String(), "origins", origins)
|
||||
|
||||
return http.Serve(listener, server.WebsocketHandler(origins))
|
||||
}
|
||||
|
||||
// newServer creates a global store and returns its RPC server.
|
||||
// Returned cleanup function should be called only if err is nil.
|
||||
func newServer(ctx *cli.Context) (server *rpc.Server, cleanup func(), err error) {
|
||||
log.PrintOrigins(true)
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(ctx.Int("verbosity")), log.StreamHandler(os.Stdout, log.TerminalFormat(false))))
|
||||
|
||||
cleanup = func() {}
|
||||
var globalStore mock.GlobalStorer
|
||||
dir := ctx.String("dir")
|
||||
if dir != "" {
|
||||
dbStore, err := db.NewGlobalStore(dir)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
cleanup = func() {
|
||||
dbStore.Close()
|
||||
}
|
||||
globalStore = dbStore
|
||||
log.Info("database global store", "dir", dir)
|
||||
} else {
|
||||
globalStore = mem.NewGlobalStore()
|
||||
log.Info("in-memory global store")
|
||||
}
|
||||
|
||||
server = rpc.NewServer()
|
||||
if err := server.RegisterName("mockStore", globalStore); err != nil {
|
||||
cleanup()
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return server, cleanup, nil
|
||||
}
|
191
cmd/swarm/global-store/global_store_test.go
Normal file
191
cmd/swarm/global-store/global_store_test.go
Normal file
@ -0,0 +1,191 @@
|
||||
// Copyright 2019 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"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
mockRPC "github.com/ethereum/go-ethereum/swarm/storage/mock/rpc"
|
||||
)
|
||||
|
||||
// TestHTTP_InMemory tests in-memory global store that exposes
|
||||
// HTTP server.
|
||||
func TestHTTP_InMemory(t *testing.T) {
|
||||
testHTTP(t, true)
|
||||
}
|
||||
|
||||
// TestHTTP_Database tests global store with persisted database
|
||||
// that exposes HTTP server.
|
||||
func TestHTTP_Database(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "swarm-global-store-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// create a fresh global store
|
||||
testHTTP(t, true, "--dir", dir)
|
||||
|
||||
// check if data saved by the previous global store instance
|
||||
testHTTP(t, false, "--dir", dir)
|
||||
}
|
||||
|
||||
// testWebsocket starts global store binary with HTTP server
|
||||
// and validates that it can store and retrieve data.
|
||||
// If put is false, no data will be stored, only retrieved,
|
||||
// giving the possibility to check if data is present in the
|
||||
// storage directory.
|
||||
func testHTTP(t *testing.T, put bool, args ...string) {
|
||||
addr := findFreeTCPAddress(t)
|
||||
testCmd := runGlobalStore(t, append([]string{"http", "--addr", addr}, args...)...)
|
||||
defer testCmd.Interrupt()
|
||||
|
||||
client, err := rpc.DialHTTP("http://" + addr)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// wait until global store process is started as
|
||||
// rpc.DialHTTP is actually not connecting
|
||||
for i := 0; i < 1000; i++ {
|
||||
_, err = http.DefaultClient.Get("http://" + addr)
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
store := mockRPC.NewGlobalStore(client)
|
||||
defer store.Close()
|
||||
|
||||
node := store.NewNodeStore(common.HexToAddress("123abc"))
|
||||
|
||||
wantKey := "key"
|
||||
wantValue := "value"
|
||||
|
||||
if put {
|
||||
err = node.Put([]byte(wantKey), []byte(wantValue))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
gotValue, err := node.Get([]byte(wantKey))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if string(gotValue) != wantValue {
|
||||
t.Errorf("got value %s for key %s, want %s", string(gotValue), wantKey, wantValue)
|
||||
}
|
||||
}
|
||||
|
||||
// TestWebsocket_InMemory tests in-memory global store that exposes
|
||||
// WebSocket server.
|
||||
func TestWebsocket_InMemory(t *testing.T) {
|
||||
testWebsocket(t, true)
|
||||
}
|
||||
|
||||
// TestWebsocket_Database tests global store with persisted database
|
||||
// that exposes HTTP server.
|
||||
func TestWebsocket_Database(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "swarm-global-store-")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// create a fresh global store
|
||||
testWebsocket(t, true, "--dir", dir)
|
||||
|
||||
// check if data saved by the previous global store instance
|
||||
testWebsocket(t, false, "--dir", dir)
|
||||
}
|
||||
|
||||
// testWebsocket starts global store binary with WebSocket server
|
||||
// and validates that it can store and retrieve data.
|
||||
// If put is false, no data will be stored, only retrieved,
|
||||
// giving the possibility to check if data is present in the
|
||||
// storage directory.
|
||||
func testWebsocket(t *testing.T, put bool, args ...string) {
|
||||
addr := findFreeTCPAddress(t)
|
||||
testCmd := runGlobalStore(t, append([]string{"ws", "--addr", addr}, args...)...)
|
||||
defer testCmd.Interrupt()
|
||||
|
||||
var client *rpc.Client
|
||||
var err error
|
||||
// wait until global store process is started
|
||||
for i := 0; i < 1000; i++ {
|
||||
client, err = rpc.DialWebsocket(context.Background(), "ws://"+addr, "")
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
store := mockRPC.NewGlobalStore(client)
|
||||
defer store.Close()
|
||||
|
||||
node := store.NewNodeStore(common.HexToAddress("123abc"))
|
||||
|
||||
wantKey := "key"
|
||||
wantValue := "value"
|
||||
|
||||
if put {
|
||||
err = node.Put([]byte(wantKey), []byte(wantValue))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
gotValue, err := node.Get([]byte(wantKey))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if string(gotValue) != wantValue {
|
||||
t.Errorf("got value %s for key %s, want %s", string(gotValue), wantKey, wantValue)
|
||||
}
|
||||
}
|
||||
|
||||
// findFreeTCPAddress returns a local address (IP:Port) to which
|
||||
// global store can listen on.
|
||||
func findFreeTCPAddress(t *testing.T) (addr string) {
|
||||
t.Helper()
|
||||
|
||||
listener, err := net.Listen("tcp", "")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer listener.Close()
|
||||
|
||||
return listener.Addr().String()
|
||||
}
|
104
cmd/swarm/global-store/main.go
Normal file
104
cmd/swarm/global-store/main.go
Normal file
@ -0,0 +1,104 @@
|
||||
// Copyright 2019 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)
|
||||
|
||||
func main() {
|
||||
err := newApp().Run(os.Args)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// newApp construct a new instance of Swarm Global Store.
|
||||
// Method Run is called on it in the main function and in tests.
|
||||
func newApp() (app *cli.App) {
|
||||
app = utils.NewApp(gitCommit, "Swarm Global Store")
|
||||
|
||||
app.Name = "global-store"
|
||||
|
||||
// app flags (for all commands)
|
||||
app.Flags = []cli.Flag{
|
||||
cli.IntFlag{
|
||||
Name: "verbosity",
|
||||
Value: 3,
|
||||
Usage: "verbosity level",
|
||||
},
|
||||
}
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
Name: "http",
|
||||
Aliases: []string{"h"},
|
||||
Usage: "start swarm global store with http server",
|
||||
Action: startHTTP,
|
||||
// Flags only for "start" command.
|
||||
// Allow app flags to be specified after the
|
||||
// command argument.
|
||||
Flags: append(app.Flags,
|
||||
cli.StringFlag{
|
||||
Name: "dir",
|
||||
Value: "",
|
||||
Usage: "data directory",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "addr",
|
||||
Value: "0.0.0.0:3033",
|
||||
Usage: "address to listen for http connection",
|
||||
},
|
||||
),
|
||||
},
|
||||
{
|
||||
Name: "websocket",
|
||||
Aliases: []string{"ws"},
|
||||
Usage: "start swarm global store with websocket server",
|
||||
Action: startWS,
|
||||
// Flags only for "start" command.
|
||||
// Allow app flags to be specified after the
|
||||
// command argument.
|
||||
Flags: append(app.Flags,
|
||||
cli.StringFlag{
|
||||
Name: "dir",
|
||||
Value: "",
|
||||
Usage: "data directory",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "addr",
|
||||
Value: "0.0.0.0:3033",
|
||||
Usage: "address to listen for websocket connection",
|
||||
},
|
||||
cli.StringSliceFlag{
|
||||
Name: "origins",
|
||||
Value: &cli.StringSlice{"*"},
|
||||
Usage: "websocket origins",
|
||||
},
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
49
cmd/swarm/global-store/run_test.go
Normal file
49
cmd/swarm/global-store/run_test.go
Normal file
@ -0,0 +1,49 @@
|
||||
// Copyright 2019 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-global-store", func() {
|
||||
if err := newApp().Run(os.Args); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
})
|
||||
}
|
||||
|
||||
func runGlobalStore(t *testing.T, args ...string) *cmdtest.TestCmd {
|
||||
tt := cmdtest.NewTestCmd(t, nil)
|
||||
tt.Run("swarm-global-store", args...)
|
||||
return tt
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
if reexec.Init() {
|
||||
return
|
||||
}
|
||||
os.Exit(m.Run())
|
||||
}
|
@ -39,13 +39,16 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/swarm"
|
||||
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
||||
swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/mock"
|
||||
mockrpc "github.com/ethereum/go-ethereum/swarm/storage/mock/rpc"
|
||||
"github.com/ethereum/go-ethereum/swarm/tracing"
|
||||
sv "github.com/ethereum/go-ethereum/swarm/version"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
const clientIdentifier = "swarm"
|
||||
@ -66,9 +69,10 @@ OPTIONS:
|
||||
{{end}}{{end}}
|
||||
`
|
||||
|
||||
var (
|
||||
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
|
||||
)
|
||||
// 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
|
||||
var (
|
||||
@ -89,6 +93,7 @@ var defaultNodeConfig = node.DefaultConfig
|
||||
|
||||
// This init function sets defaults so cmd/swarm can run alongside geth.
|
||||
func init() {
|
||||
sv.GitCommit = gitCommit
|
||||
defaultNodeConfig.Name = clientIdentifier
|
||||
defaultNodeConfig.Version = sv.VersionWithCommit(gitCommit)
|
||||
defaultNodeConfig.P2P.ListenAddr = ":30399"
|
||||
@ -140,6 +145,8 @@ func init() {
|
||||
dbCommand,
|
||||
// See config.go
|
||||
DumpConfigCommand,
|
||||
// hashesCommand
|
||||
hashesCommand,
|
||||
}
|
||||
|
||||
// append a hidden help subcommand to all commands that have subcommands
|
||||
@ -154,7 +161,6 @@ func init() {
|
||||
utils.BootnodesFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.ListenPortFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
@ -187,10 +193,13 @@ func init() {
|
||||
SwarmUploadDefaultPath,
|
||||
SwarmUpFromStdinFlag,
|
||||
SwarmUploadMimeType,
|
||||
// bootnode mode
|
||||
SwarmBootnodeModeFlag,
|
||||
// storage flags
|
||||
SwarmStorePath,
|
||||
SwarmStoreCapacity,
|
||||
SwarmStoreCacheCapacity,
|
||||
SwarmGlobalStoreAPIFlag,
|
||||
}
|
||||
rpcFlags := []cli.Flag{
|
||||
utils.WSEnabledFlag,
|
||||
@ -227,12 +236,17 @@ func main() {
|
||||
|
||||
func keys(ctx *cli.Context) error {
|
||||
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))
|
||||
bzzkey := crypto.Keccak256Hash(pubkey).Hex()
|
||||
|
||||
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))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -272,6 +286,10 @@ func bzzd(ctx *cli.Context) error {
|
||||
setSwarmBootstrapNodes(ctx, &cfg)
|
||||
//setup the ethereum node
|
||||
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)
|
||||
if err != nil {
|
||||
utils.Fatalf("can't create node: %v", err)
|
||||
@ -294,6 +312,15 @@ func bzzd(ctx *cli.Context) error {
|
||||
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()
|
||||
return nil
|
||||
}
|
||||
@ -301,8 +328,18 @@ func bzzd(ctx *cli.Context) error {
|
||||
func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) {
|
||||
//define the swarm service boot function
|
||||
boot := func(_ *node.ServiceContext) (node.Service, error) {
|
||||
// In production, mockStore must be always nil.
|
||||
return swarm.NewSwarm(bzzconfig, nil)
|
||||
var nodeStore *mock.NodeStore
|
||||
if bzzconfig.GlobalStoreAPI != "" {
|
||||
// connect to global store
|
||||
client, err := rpc.Dial(bzzconfig.GlobalStoreAPI)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("global store: %v", err)
|
||||
}
|
||||
globalStore := mockrpc.NewGlobalStore(client)
|
||||
// create a node store for this swarm key on global store
|
||||
nodeStore = globalStore.NewNodeStore(common.HexToAddress(bzzconfig.BzzKey))
|
||||
}
|
||||
return swarm.NewSwarm(bzzconfig, nodeStore)
|
||||
}
|
||||
//register within the ethereum node
|
||||
if err := stack.Register(boot); err != nil {
|
||||
@ -428,5 +465,5 @@ func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) {
|
||||
}
|
||||
cfg.P2P.BootstrapNodes = append(cfg.P2P.BootstrapNodes, node)
|
||||
}
|
||||
log.Debug("added default swarm bootnodes", "length", len(cfg.P2P.BootstrapNodes))
|
||||
|
||||
}
|
||||
|
@ -254,7 +254,6 @@ func existingTestNode(t *testing.T, dir string, bzzaccount string) *testNode {
|
||||
node.Cmd = runSwarm(t,
|
||||
"--port", p2pPort,
|
||||
"--nat", "extip:127.0.0.1",
|
||||
"--nodiscover",
|
||||
"--datadir", dir,
|
||||
"--ipcpath", conf.IPCPath,
|
||||
"--ens-api", "",
|
||||
@ -330,7 +329,6 @@ func newTestNode(t *testing.T, dir string) *testNode {
|
||||
node.Cmd = runSwarm(t,
|
||||
"--port", p2pPort,
|
||||
"--nat", "extip:127.0.0.1",
|
||||
"--nodiscover",
|
||||
"--datadir", dir,
|
||||
"--ipcpath", conf.IPCPath,
|
||||
"--ens-api", "",
|
||||
|
@ -2,13 +2,10 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
@ -19,12 +16,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"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/testutil"
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/pborman/uuid"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
@ -33,34 +26,28 @@ const (
|
||||
feedRandomDataLength = 8
|
||||
)
|
||||
|
||||
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)))))
|
||||
|
||||
func feedUploadAndSyncCmd(ctx *cli.Context, tuid string) error {
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
errc <- feedUploadAndSync(c)
|
||||
errc <- feedUploadAndSync(ctx, tuid)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
metrics.GetOrRegisterCounter("feed-and-sync.fail", nil).Inc(1)
|
||||
metrics.GetOrRegisterCounter(fmt.Sprintf("%s.fail", commandName), nil).Inc(1)
|
||||
}
|
||||
return err
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
metrics.GetOrRegisterCounter("feed-and-sync.timeout", nil).Inc(1)
|
||||
metrics.GetOrRegisterCounter(fmt.Sprintf("%s.timeout", commandName), 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())
|
||||
|
||||
generateEndpoints(scheme, cluster, appName, from, to)
|
||||
|
||||
log.Info("generating and uploading feeds to " + endpoints[0] + " and syncing")
|
||||
func feedUploadAndSync(c *cli.Context, tuid string) error {
|
||||
log.Info("generating and uploading feeds to " + httpEndpoint(hosts[0]) + " and syncing")
|
||||
|
||||
// create a random private key to sign updates with and derive the address
|
||||
pkFile, err := ioutil.TempFile("", "swarm-feed-smoke-test")
|
||||
@ -114,7 +101,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
|
||||
// create feed manifest, topic only
|
||||
var out bytes.Buffer
|
||||
cmd := exec.Command("swarm", "--bzzapi", endpoints[0], "feed", "create", "--topic", topicHex, "--user", userHex)
|
||||
cmd := exec.Command("swarm", "--bzzapi", httpEndpoint(hosts[0]), "feed", "create", "--topic", topicHex, "--user", userHex)
|
||||
cmd.Stdout = &out
|
||||
log.Debug("create feed manifest topic cmd", "cmd", cmd)
|
||||
err = cmd.Run()
|
||||
@ -129,7 +116,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
out.Reset()
|
||||
|
||||
// create feed manifest, subtopic only
|
||||
cmd = exec.Command("swarm", "--bzzapi", endpoints[0], "feed", "create", "--name", subTopicHex, "--user", userHex)
|
||||
cmd = exec.Command("swarm", "--bzzapi", httpEndpoint(hosts[0]), "feed", "create", "--name", subTopicHex, "--user", userHex)
|
||||
cmd.Stdout = &out
|
||||
log.Debug("create feed manifest subtopic cmd", "cmd", cmd)
|
||||
err = cmd.Run()
|
||||
@ -144,7 +131,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
out.Reset()
|
||||
|
||||
// create feed manifest, merged topic
|
||||
cmd = exec.Command("swarm", "--bzzapi", endpoints[0], "feed", "create", "--topic", topicHex, "--name", subTopicHex, "--user", userHex)
|
||||
cmd = exec.Command("swarm", "--bzzapi", httpEndpoint(hosts[0]), "feed", "create", "--topic", topicHex, "--name", subTopicHex, "--user", userHex)
|
||||
cmd.Stdout = &out
|
||||
log.Debug("create feed manifest mergetopic cmd", "cmd", cmd)
|
||||
err = cmd.Run()
|
||||
@ -170,7 +157,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
dataHex := hexutil.Encode(data)
|
||||
|
||||
// update with topic
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", endpoints[0], "feed", "update", "--topic", topicHex, dataHex)
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", httpEndpoint(hosts[0]), "feed", "update", "--topic", topicHex, dataHex)
|
||||
cmd.Stdout = &out
|
||||
log.Debug("update feed manifest topic cmd", "cmd", cmd)
|
||||
err = cmd.Run()
|
||||
@ -181,7 +168,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
out.Reset()
|
||||
|
||||
// update with subtopic
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", endpoints[0], "feed", "update", "--name", subTopicHex, dataHex)
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", httpEndpoint(hosts[0]), "feed", "update", "--name", subTopicHex, dataHex)
|
||||
cmd.Stdout = &out
|
||||
log.Debug("update feed manifest subtopic cmd", "cmd", cmd)
|
||||
err = cmd.Run()
|
||||
@ -192,7 +179,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
out.Reset()
|
||||
|
||||
// update with merged topic
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", endpoints[0], "feed", "update", "--topic", topicHex, "--name", subTopicHex, dataHex)
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", httpEndpoint(hosts[0]), "feed", "update", "--topic", topicHex, "--name", subTopicHex, dataHex)
|
||||
cmd.Stdout = &out
|
||||
log.Debug("update feed manifest merged topic cmd", "cmd", cmd)
|
||||
err = cmd.Run()
|
||||
@ -206,14 +193,14 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
|
||||
// retrieve the data
|
||||
wg := sync.WaitGroup{}
|
||||
for _, endpoint := range endpoints {
|
||||
for _, host := range hosts {
|
||||
// raw retrieve, topic only
|
||||
for _, hex := range []string{topicHex, subTopicOnlyHex, mergedSubTopicHex} {
|
||||
wg.Add(1)
|
||||
ruid := uuid.New()[:8]
|
||||
go func(hex string, endpoint string, ruid string) {
|
||||
for {
|
||||
err := fetchFeed(hex, userHex, endpoint, dataHash, ruid)
|
||||
err := fetchFeed(hex, userHex, httpEndpoint(host), dataHash, ruid)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -221,20 +208,18 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}(hex, endpoint, ruid)
|
||||
|
||||
}(hex, httpEndpoint(host), ruid)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
log.Info("all endpoints synced random data successfully")
|
||||
|
||||
// upload test file
|
||||
seed := int(time.Now().UnixNano() / 1e6)
|
||||
log.Info("feed uploading to "+endpoints[0]+" and syncing", "seed", seed)
|
||||
log.Info("feed uploading to "+httpEndpoint(hosts[0])+" and syncing", "seed", seed)
|
||||
|
||||
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||
|
||||
hash, err := upload(&randomBytes, endpoints[0])
|
||||
hash, err := upload(randomBytes, httpEndpoint(hosts[0]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -243,15 +228,12 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
multihashHex := hexutil.Encode(hashBytes)
|
||||
fileHash, err := digest(bytes.NewReader(randomBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fileHash := h.Sum(nil)
|
||||
|
||||
log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fileHash))
|
||||
|
||||
// update file with topic
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", endpoints[0], "feed", "update", "--topic", topicHex, multihashHex)
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", httpEndpoint(hosts[0]), "feed", "update", "--topic", topicHex, multihashHex)
|
||||
cmd.Stdout = &out
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
@ -261,7 +243,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
out.Reset()
|
||||
|
||||
// update file with subtopic
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", endpoints[0], "feed", "update", "--name", subTopicHex, multihashHex)
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", httpEndpoint(hosts[0]), "feed", "update", "--name", subTopicHex, multihashHex)
|
||||
cmd.Stdout = &out
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
@ -271,7 +253,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
out.Reset()
|
||||
|
||||
// update file with merged topic
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", endpoints[0], "feed", "update", "--topic", topicHex, "--name", subTopicHex, multihashHex)
|
||||
cmd = exec.Command("swarm", "--bzzaccount", pkFile.Name(), "--bzzapi", httpEndpoint(hosts[0]), "feed", "update", "--topic", topicHex, "--name", subTopicHex, multihashHex)
|
||||
cmd.Stdout = &out
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
@ -282,7 +264,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
for _, endpoint := range endpoints {
|
||||
for _, host := range hosts {
|
||||
|
||||
// manifest retrieve, topic only
|
||||
for _, url := range []string{manifestWithTopic, manifestWithSubTopic, manifestWithMergedTopic} {
|
||||
@ -290,7 +272,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
ruid := uuid.New()[:8]
|
||||
go func(url string, endpoint string, ruid string) {
|
||||
for {
|
||||
err := fetch(url, endpoint, fileHash, ruid)
|
||||
err := fetch(url, endpoint, fileHash, ruid, "")
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
@ -298,7 +280,7 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}(url, endpoint, ruid)
|
||||
}(url, httpEndpoint(host), ruid)
|
||||
}
|
||||
|
||||
}
|
||||
@ -307,60 +289,3 @@ func feedUploadAndSync(c *cli.Context) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
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)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
log.Trace("http get request (feed)", "ruid", ruid, "api", endpoint, "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 {
|
||||
log.Error(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("http get response (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user, "code", res.StatusCode, "len", res.ContentLength)
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return fmt.Errorf("expected status code %d, got %v (ruid %v)", 200, res.StatusCode, ruid)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
rdigest, err := digest(res.Body)
|
||||
if err != nil {
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(rdigest, original) {
|
||||
err := fmt.Errorf("downloaded imported file md5=%x is not the same as the generated one=%x", rdigest, original)
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("downloaded file matches random file", "ruid", ruid, "len", res.ContentLength)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -37,18 +37,16 @@ var (
|
||||
)
|
||||
|
||||
var (
|
||||
endpoints []string
|
||||
includeLocalhost bool
|
||||
cluster string
|
||||
appName string
|
||||
scheme string
|
||||
filesize int
|
||||
syncDelay int
|
||||
from int
|
||||
to int
|
||||
verbosity int
|
||||
timeout int
|
||||
single bool
|
||||
allhosts string
|
||||
hosts []string
|
||||
filesize int
|
||||
syncDelay int
|
||||
httpPort int
|
||||
wsPort int
|
||||
verbosity int
|
||||
timeout int
|
||||
single bool
|
||||
trackTimeout int
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -59,39 +57,22 @@ func main() {
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "cluster-endpoint",
|
||||
Value: "prod",
|
||||
Usage: "cluster to point to (prod or a given namespace)",
|
||||
Destination: &cluster,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "app",
|
||||
Value: "swarm",
|
||||
Usage: "application to point to (swarm or swarm-private)",
|
||||
Destination: &appName,
|
||||
Name: "hosts",
|
||||
Value: "",
|
||||
Usage: "comma-separated list of swarm hosts",
|
||||
Destination: &allhosts,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "cluster-from",
|
||||
Value: 8501,
|
||||
Usage: "swarm node (from)",
|
||||
Destination: &from,
|
||||
Name: "http-port",
|
||||
Value: 80,
|
||||
Usage: "http port",
|
||||
Destination: &httpPort,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "cluster-to",
|
||||
Value: 8512,
|
||||
Usage: "swarm node (to)",
|
||||
Destination: &to,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "cluster-scheme",
|
||||
Value: "http",
|
||||
Usage: "http or https",
|
||||
Destination: &scheme,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "include-localhost",
|
||||
Usage: "whether to include localhost:8500 as an endpoint",
|
||||
Destination: &includeLocalhost,
|
||||
Name: "ws-port",
|
||||
Value: 8546,
|
||||
Usage: "ws port",
|
||||
Destination: &wsPort,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "filesize",
|
||||
@ -122,6 +103,12 @@ func main() {
|
||||
Usage: "whether to fetch content from a single node or from all nodes",
|
||||
Destination: &single,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "track-timeout",
|
||||
Value: 5,
|
||||
Usage: "timeout in seconds to wait for GetAllReferences to return",
|
||||
Destination: &trackTimeout,
|
||||
},
|
||||
}
|
||||
|
||||
app.Flags = append(app.Flags, []cli.Flag{
|
||||
@ -130,7 +117,7 @@ func main() {
|
||||
swarmmetrics.MetricsInfluxDBDatabaseFlag,
|
||||
swarmmetrics.MetricsInfluxDBUsernameFlag,
|
||||
swarmmetrics.MetricsInfluxDBPasswordFlag,
|
||||
swarmmetrics.MetricsInfluxDBHostTagFlag,
|
||||
swarmmetrics.MetricsInfluxDBTagsFlag,
|
||||
}...)
|
||||
|
||||
app.Flags = append(app.Flags, tracing.Flags...)
|
||||
@ -140,13 +127,25 @@ func main() {
|
||||
Name: "upload_and_sync",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "upload and sync",
|
||||
Action: cliUploadAndSync,
|
||||
Action: wrapCliCommand("upload-and-sync", uploadAndSyncCmd),
|
||||
},
|
||||
{
|
||||
Name: "feed_sync",
|
||||
Aliases: []string{"f"},
|
||||
Usage: "feed update generate, upload and sync",
|
||||
Action: cliFeedUploadAndSync,
|
||||
Action: wrapCliCommand("feed-and-sync", feedUploadAndSyncCmd),
|
||||
},
|
||||
{
|
||||
Name: "upload_speed",
|
||||
Aliases: []string{"u"},
|
||||
Usage: "measure upload speed",
|
||||
Action: wrapCliCommand("upload-speed", uploadSpeedCmd),
|
||||
},
|
||||
{
|
||||
Name: "sliding_window",
|
||||
Aliases: []string{"s"},
|
||||
Usage: "measure network aggregate capacity",
|
||||
Action: wrapCliCommand("sliding-window", slidingWindowCmd),
|
||||
},
|
||||
}
|
||||
|
||||
@ -177,13 +176,14 @@ func emitMetrics(ctx *cli.Context) error {
|
||||
database = ctx.GlobalString(swarmmetrics.MetricsInfluxDBDatabaseFlag.Name)
|
||||
username = ctx.GlobalString(swarmmetrics.MetricsInfluxDBUsernameFlag.Name)
|
||||
password = ctx.GlobalString(swarmmetrics.MetricsInfluxDBPasswordFlag.Name)
|
||||
hosttag = ctx.GlobalString(swarmmetrics.MetricsInfluxDBHostTagFlag.Name)
|
||||
tags = ctx.GlobalString(swarmmetrics.MetricsInfluxDBTagsFlag.Name)
|
||||
)
|
||||
return influxdb.InfluxDBWithTagsOnce(gethmetrics.DefaultRegistry, endpoint, database, username, password, "swarm-smoke.", map[string]string{
|
||||
"host": hosttag,
|
||||
"version": gitCommit,
|
||||
"filesize": fmt.Sprintf("%v", filesize),
|
||||
})
|
||||
|
||||
tagsMap := utils.SplitTagsFlag(tags)
|
||||
tagsMap["version"] = gitCommit
|
||||
tagsMap["filesize"] = fmt.Sprintf("%v", filesize)
|
||||
|
||||
return influxdb.InfluxDBWithTagsOnce(gethmetrics.DefaultRegistry, endpoint, database, username, password, "swarm-smoke.", tagsMap)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
131
cmd/swarm/swarm-smoke/sliding_window.go
Normal file
131
cmd/swarm/swarm-smoke/sliding_window.go
Normal file
@ -0,0 +1,131 @@
|
||||
// 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"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
type uploadResult struct {
|
||||
hash string
|
||||
digest []byte
|
||||
}
|
||||
|
||||
func slidingWindowCmd(ctx *cli.Context, tuid string) error {
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
errc <- slidingWindow(ctx, tuid)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
metrics.GetOrRegisterCounter(fmt.Sprintf("%s.fail", commandName), nil).Inc(1)
|
||||
}
|
||||
return err
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
metrics.GetOrRegisterCounter(fmt.Sprintf("%s.timeout", commandName), nil).Inc(1)
|
||||
|
||||
return fmt.Errorf("timeout after %v sec", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func slidingWindow(ctx *cli.Context, tuid string) error {
|
||||
hashes := []uploadResult{} //swarm hashes of the uploads
|
||||
nodes := len(hosts)
|
||||
const iterationTimeout = 30 * time.Second
|
||||
log.Info("sliding window test started", "tuid", tuid, "nodes", nodes, "filesize(kb)", filesize, "timeout", timeout)
|
||||
uploadedBytes := 0
|
||||
networkDepth := 0
|
||||
errored := false
|
||||
|
||||
outer:
|
||||
for {
|
||||
log.Info("uploading to "+httpEndpoint(hosts[0])+" and syncing", "seed", seed)
|
||||
|
||||
t1 := time.Now()
|
||||
|
||||
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||
|
||||
hash, err := upload(randomBytes, httpEndpoint(hosts[0]))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
metrics.GetOrRegisterResettingTimer("sliding-window.upload-time", nil).UpdateSince(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), "sleeping", syncDelay)
|
||||
hashes = append(hashes, uploadResult{hash: hash, digest: fhash})
|
||||
time.Sleep(time.Duration(syncDelay) * time.Second)
|
||||
uploadedBytes += filesize * 1000
|
||||
|
||||
for i, v := range hashes {
|
||||
timeout := time.After(time.Duration(timeout) * time.Second)
|
||||
errored = false
|
||||
|
||||
inner:
|
||||
for {
|
||||
select {
|
||||
case <-timeout:
|
||||
errored = true
|
||||
log.Error("error retrieving hash. timeout", "hash idx", i, "err", err)
|
||||
metrics.GetOrRegisterCounter("sliding-window.single.error", nil).Inc(1)
|
||||
break inner
|
||||
default:
|
||||
idx := 1 + rand.Intn(len(hosts)-1)
|
||||
ruid := uuid.New()[:8]
|
||||
start := time.Now()
|
||||
err := fetch(v.hash, httpEndpoint(hosts[idx]), v.digest, ruid, "")
|
||||
if err != nil {
|
||||
continue inner
|
||||
}
|
||||
metrics.GetOrRegisterResettingTimer("sliding-window.single.fetch-time", nil).UpdateSince(start)
|
||||
break inner
|
||||
}
|
||||
}
|
||||
|
||||
if errored {
|
||||
break outer
|
||||
}
|
||||
networkDepth = i
|
||||
metrics.GetOrRegisterGauge("sliding-window.network-depth", nil).Update(int64(networkDepth))
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("sliding window test finished", "errored?", errored, "networkDepth", networkDepth, "networkDepth(kb)", networkDepth*filesize)
|
||||
log.Info("stats", "uploadedFiles", len(hashes), "uploadedKb", uploadedBytes/1000, "filesizeKb", filesize)
|
||||
|
||||
return nil
|
||||
}
|
@ -19,91 +19,122 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
crand "crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"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/storage"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
func generateEndpoints(scheme string, cluster string, app string, from int, to int) {
|
||||
if cluster == "prod" {
|
||||
for port := from; port < to; port++ {
|
||||
endpoints = append(endpoints, fmt.Sprintf("%s://%v.swarm-gateways.net", scheme, port))
|
||||
}
|
||||
} else {
|
||||
for port := from; port < to; port++ {
|
||||
endpoints = append(endpoints, fmt.Sprintf("%s://%s-%v-%s.stg.swarm-gateways.net", scheme, app, port, cluster))
|
||||
}
|
||||
}
|
||||
|
||||
if includeLocalhost {
|
||||
endpoints = append(endpoints, "http://localhost:8500")
|
||||
}
|
||||
}
|
||||
|
||||
func cliUploadAndSync(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-and-sync", nil).Inc(1)
|
||||
func uploadAndSyncCmd(ctx *cli.Context, tuid string) error {
|
||||
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
errc <- uploadAndSync(c)
|
||||
errc <- uplaodAndSync(ctx, randomBytes, tuid)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
metrics.GetOrRegisterCounter("upload-and-sync.fail", nil).Inc(1)
|
||||
metrics.GetOrRegisterCounter(fmt.Sprintf("%s.fail", commandName), 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)
|
||||
metrics.GetOrRegisterCounter(fmt.Sprintf("%s.timeout", commandName), nil).Inc(1)
|
||||
|
||||
e := fmt.Errorf("timeout after %v sec", timeout)
|
||||
// trigger debug functionality on randomBytes
|
||||
err := trackChunks(randomBytes[:])
|
||||
if err != nil {
|
||||
e = fmt.Errorf("%v; triggerChunkDebug failed: %v", e, err)
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
}
|
||||
|
||||
func uploadAndSync(c *cli.Context) error {
|
||||
defer func(now time.Time) {
|
||||
totalTime := time.Since(now)
|
||||
func trackChunks(testData []byte) error {
|
||||
log.Warn("Test timed out; running chunk debug sequence")
|
||||
|
||||
log.Info("total time", "time", totalTime, "kb", filesize)
|
||||
metrics.GetOrRegisterCounter("upload-and-sync.total-time", nil).Inc(int64(totalTime))
|
||||
}(time.Now())
|
||||
addrs, err := getAllRefs(testData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Trace("All references retrieved")
|
||||
|
||||
generateEndpoints(scheme, cluster, appName, from, to)
|
||||
seed := int(time.Now().UnixNano() / 1e6)
|
||||
log.Info("uploading to "+endpoints[0]+" and syncing", "seed", seed)
|
||||
// has-chunks
|
||||
for _, host := range hosts {
|
||||
httpHost := fmt.Sprintf("ws://%s:%d", host, 8546)
|
||||
log.Trace("Calling `Has` on host", "httpHost", httpHost)
|
||||
rpcClient, err := rpc.Dial(httpHost)
|
||||
if err != nil {
|
||||
log.Trace("Error dialing host", "err", err)
|
||||
return err
|
||||
}
|
||||
log.Trace("rpc dial ok")
|
||||
var hasInfo []api.HasInfo
|
||||
err = rpcClient.Call(&hasInfo, "bzz_has", addrs)
|
||||
if err != nil {
|
||||
log.Trace("Error calling host", "err", err)
|
||||
return err
|
||||
}
|
||||
log.Trace("rpc call ok")
|
||||
count := 0
|
||||
for _, info := range hasInfo {
|
||||
if !info.Has {
|
||||
count++
|
||||
log.Error("Host does not have chunk", "host", httpHost, "chunk", info.Addr)
|
||||
}
|
||||
}
|
||||
if count == 0 {
|
||||
log.Info("Host reported to have all chunks", "host", httpHost)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||
func getAllRefs(testData []byte) (storage.AddressCollection, error) {
|
||||
log.Trace("Getting all references for given root hash")
|
||||
datadir, err := ioutil.TempDir("", "chunk-debug")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create temp dir: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(datadir)
|
||||
fileStore, err := storage.NewLocalFileStore(datadir, make([]byte, 32))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(trackTimeout)*time.Second)
|
||||
defer cancel()
|
||||
|
||||
reader := bytes.NewReader(testData)
|
||||
return fileStore.GetAllReferences(ctx, reader, false)
|
||||
}
|
||||
|
||||
func uplaodAndSync(c *cli.Context, randomBytes []byte, tuid string) error {
|
||||
log.Info("uploading to "+httpEndpoint(hosts[0])+" and syncing", "tuid", tuid, "seed", seed)
|
||||
|
||||
t1 := time.Now()
|
||||
hash, err := upload(&randomBytes, endpoints[0])
|
||||
hash, err := upload(randomBytes, httpEndpoint(hosts[0]))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
metrics.GetOrRegisterCounter("upload-and-sync.upload-time", nil).Inc(int64(time.Since(t1)))
|
||||
t2 := time.Since(t1)
|
||||
metrics.GetOrRegisterResettingTimer("upload-and-sync.upload-time", nil).Update(t2)
|
||||
|
||||
fhash, err := digest(bytes.NewReader(randomBytes))
|
||||
if err != nil {
|
||||
@ -111,147 +142,53 @@ func uploadAndSync(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash))
|
||||
log.Info("uploaded successfully", "tuid", tuid, "hash", hash, "took", t2, "digest", fmt.Sprintf("%x", fhash))
|
||||
|
||||
time.Sleep(time.Duration(syncDelay) * time.Second)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
if single {
|
||||
rand.Seed(time.Now().UTC().UnixNano())
|
||||
randIndex := 1 + rand.Intn(len(endpoints)-1)
|
||||
randIndex := 1 + rand.Intn(len(hosts)-1)
|
||||
ruid := uuid.New()[:8]
|
||||
wg.Add(1)
|
||||
go func(endpoint string, ruid string) {
|
||||
for {
|
||||
start := time.Now()
|
||||
err := fetch(hash, endpoint, fhash, ruid)
|
||||
fetchTime := time.Since(start)
|
||||
err := fetch(hash, endpoint, fhash, ruid, tuid)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ended := time.Since(start)
|
||||
|
||||
metrics.GetOrRegisterMeter("upload-and-sync.single.fetch-time", nil).Mark(int64(fetchTime))
|
||||
metrics.GetOrRegisterResettingTimer("upload-and-sync.single.fetch-time", nil).Update(ended)
|
||||
log.Info("fetch successful", "tuid", tuid, "ruid", ruid, "took", ended, "endpoint", endpoint)
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}(endpoints[randIndex], ruid)
|
||||
}(httpEndpoint(hosts[randIndex]), ruid)
|
||||
} else {
|
||||
for _, endpoint := range endpoints {
|
||||
for _, endpoint := range hosts[1:] {
|
||||
ruid := uuid.New()[:8]
|
||||
wg.Add(1)
|
||||
go func(endpoint string, ruid string) {
|
||||
for {
|
||||
start := time.Now()
|
||||
err := fetch(hash, endpoint, fhash, ruid)
|
||||
fetchTime := time.Since(start)
|
||||
err := fetch(hash, endpoint, fhash, ruid, tuid)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ended := time.Since(start)
|
||||
|
||||
metrics.GetOrRegisterMeter("upload-and-sync.each.fetch-time", nil).Mark(int64(fetchTime))
|
||||
metrics.GetOrRegisterResettingTimer("upload-and-sync.each.fetch-time", nil).Update(ended)
|
||||
log.Info("fetch successful", "tuid", tuid, "ruid", ruid, "took", ended, "endpoint", endpoint)
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}(endpoint, ruid)
|
||||
}(httpEndpoint(endpoint), ruid)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
log.Info("all endpoints synced random file successfully")
|
||||
log.Info("all hosts synced random file successfully")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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 {
|
||||
ctx, sp := spancontext.StartSpan(context.Background(), "upload-and-sync.fetch")
|
||||
defer sp.Finish()
|
||||
|
||||
log.Trace("sleeping", "ruid", ruid)
|
||||
time.Sleep(3 * time.Second)
|
||||
log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash)
|
||||
|
||||
var tn time.Time
|
||||
reqUri := 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 {
|
||||
log.Error(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
log.Trace("http get response", "ruid", ruid, "api", endpoint, "hash", hash, "code", res.StatusCode, "len", res.ContentLength)
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
err := fmt.Errorf("expected status code %d, got %v", 200, res.StatusCode)
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
rdigest, err := digest(res.Body)
|
||||
if err != nil {
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(rdigest, original) {
|
||||
err := fmt.Errorf("downloaded imported file md5=%x is not the same as the generated one=%x", rdigest, original)
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("downloaded file matches random file", "ruid", ruid, "len", res.ContentLength)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// upload is uploading a file `f` to `endpoint` via the `swarm up` cmd
|
||||
func upload(dataBytes *[]byte, endpoint string) (string, error) {
|
||||
swarm := client.NewClient(endpoint)
|
||||
f := &client.File{
|
||||
ReadCloser: ioutil.NopCloser(bytes.NewReader(*dataBytes)),
|
||||
ManifestEntry: api.ManifestEntry{
|
||||
ContentType: "text/plain",
|
||||
Mode: 0660,
|
||||
Size: int64(len(*dataBytes)),
|
||||
},
|
||||
}
|
||||
|
||||
// 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) {
|
||||
h := md5.New()
|
||||
_, err := io.Copy(h, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
// generates random data in heap buffer
|
||||
func generateRandomData(datasize int) ([]byte, error) {
|
||||
b := make([]byte, datasize)
|
||||
c, err := crand.Read(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if c != datasize {
|
||||
return nil, errors.New("short read")
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
|
73
cmd/swarm/swarm-smoke/upload_speed.go
Normal file
73
cmd/swarm/swarm-smoke/upload_speed.go
Normal file
@ -0,0 +1,73 @@
|
||||
// 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"
|
||||
"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"
|
||||
)
|
||||
|
||||
func uploadSpeedCmd(ctx *cli.Context, tuid string) error {
|
||||
log.Info("uploading to "+hosts[0], "tuid", tuid, "seed", seed)
|
||||
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
errc <- uploadSpeed(ctx, tuid, randomBytes)
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errc:
|
||||
if err != nil {
|
||||
metrics.GetOrRegisterCounter(fmt.Sprintf("%s.fail", commandName), nil).Inc(1)
|
||||
}
|
||||
return err
|
||||
case <-time.After(time.Duration(timeout) * time.Second):
|
||||
metrics.GetOrRegisterCounter(fmt.Sprintf("%s.timeout", commandName), nil).Inc(1)
|
||||
|
||||
// trigger debug functionality on randomBytes
|
||||
|
||||
return fmt.Errorf("timeout after %v sec", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func uploadSpeed(c *cli.Context, tuid string, data []byte) error {
|
||||
t1 := time.Now()
|
||||
hash, err := upload(data, hosts[0])
|
||||
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(data))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash))
|
||||
return nil
|
||||
}
|
235
cmd/swarm/swarm-smoke/util.go
Normal file
235
cmd/swarm/swarm-smoke/util.go
Normal file
@ -0,0 +1,235 @@
|
||||
// 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"
|
||||
"context"
|
||||
"crypto/md5"
|
||||
crand "crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"net/http/httptrace"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"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"
|
||||
opentracing "github.com/opentracing/opentracing-go"
|
||||
"github.com/pborman/uuid"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
commandName = ""
|
||||
seed = int(time.Now().UTC().UnixNano())
|
||||
)
|
||||
|
||||
func init() {
|
||||
rand.Seed(int64(seed))
|
||||
}
|
||||
|
||||
func httpEndpoint(host string) string {
|
||||
return fmt.Sprintf("http://%s:%d", host, httpPort)
|
||||
}
|
||||
|
||||
func wsEndpoint(host string) string {
|
||||
return fmt.Sprintf("ws://%s:%d", host, wsPort)
|
||||
}
|
||||
|
||||
func wrapCliCommand(name string, command func(*cli.Context, string) error) func(*cli.Context) error {
|
||||
return func(ctx *cli.Context) error {
|
||||
log.PrintOrigins(true)
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(os.Stdout, log.TerminalFormat(false))))
|
||||
|
||||
// test uuid
|
||||
tuid := uuid.New()[:8]
|
||||
|
||||
commandName = name
|
||||
|
||||
hosts = strings.Split(allhosts, ",")
|
||||
|
||||
defer func(now time.Time) {
|
||||
totalTime := time.Since(now)
|
||||
log.Info("total time", "tuid", tuid, "time", totalTime, "kb", filesize)
|
||||
metrics.GetOrRegisterResettingTimer(name+".total-time", nil).Update(totalTime)
|
||||
}(time.Now())
|
||||
|
||||
log.Info("smoke test starting", "tuid", tuid, "task", name, "timeout", timeout)
|
||||
metrics.GetOrRegisterCounter(name, nil).Inc(1)
|
||||
|
||||
return command(ctx, tuid)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
log.Trace("http get request (feed)", "ruid", ruid, "api", endpoint, "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 {
|
||||
log.Error(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("http get response (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user, "code", res.StatusCode, "len", res.ContentLength)
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return fmt.Errorf("expected status code %d, got %v (ruid %v)", 200, res.StatusCode, ruid)
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
rdigest, err := digest(res.Body)
|
||||
if err != nil {
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(rdigest, original) {
|
||||
err := fmt.Errorf("downloaded imported file md5=%x is not the same as the generated one=%x", rdigest, original)
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("downloaded file matches random file", "ruid", ruid, "len", res.ContentLength)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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, tuid string) error {
|
||||
ctx, sp := spancontext.StartSpan(context.Background(), "upload-and-sync.fetch")
|
||||
defer sp.Finish()
|
||||
|
||||
log.Info("http get request", "tuid", tuid, "ruid", ruid, "endpoint", endpoint, "hash", hash)
|
||||
|
||||
var tn time.Time
|
||||
reqUri := endpoint + "/bzz:/" + hash + "/"
|
||||
req, _ := http.NewRequest("GET", reqUri, nil)
|
||||
|
||||
opentracing.GlobalTracer().Inject(
|
||||
sp.Context(),
|
||||
opentracing.HTTPHeaders,
|
||||
opentracing.HTTPHeadersCarrier(req.Header))
|
||||
|
||||
trace := client.GetClientTrace(commandName+" - http get", commandName, 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 {
|
||||
log.Error(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
log.Info("http get response", "tuid", tuid, "ruid", ruid, "endpoint", endpoint, "hash", hash, "code", res.StatusCode, "len", res.ContentLength)
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
err := fmt.Errorf("expected status code %d, got %v", 200, res.StatusCode)
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
defer res.Body.Close()
|
||||
|
||||
rdigest, err := digest(res.Body)
|
||||
if err != nil {
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
if !bytes.Equal(rdigest, original) {
|
||||
err := fmt.Errorf("downloaded imported file md5=%x is not the same as the generated one=%x", rdigest, original)
|
||||
log.Warn(err.Error(), "ruid", ruid)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Trace("downloaded file matches random file", "ruid", ruid, "len", res.ContentLength)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// upload an arbitrary byte as a plaintext file to `endpoint` using the api client
|
||||
func upload(data []byte, endpoint string) (string, error) {
|
||||
swarm := client.NewClient(endpoint)
|
||||
f := &client.File{
|
||||
ReadCloser: ioutil.NopCloser(bytes.NewReader(data)),
|
||||
ManifestEntry: api.ManifestEntry{
|
||||
ContentType: "text/plain",
|
||||
Mode: 0660,
|
||||
Size: int64(len(data)),
|
||||
},
|
||||
}
|
||||
|
||||
// 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) {
|
||||
h := md5.New()
|
||||
_, err := io.Copy(h, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
|
||||
// generates random data in heap buffer
|
||||
func generateRandomData(datasize int) ([]byte, error) {
|
||||
b := make([]byte, datasize)
|
||||
c, err := crand.Read(b)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if c != datasize {
|
||||
return nil, errors.New("short read")
|
||||
}
|
||||
return b, nil
|
||||
}
|
157
cmd/swarm/swarm-snapshot/create.go
Normal file
157
cmd/swarm/swarm-snapshot/create.go
Normal 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
|
||||
}
|
143
cmd/swarm/swarm-snapshot/create_test.go
Normal file
143
cmd/swarm/swarm-snapshot/create_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
82
cmd/swarm/swarm-snapshot/main.go
Normal file
82
cmd/swarm/swarm-snapshot/main.go
Normal 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
|
||||
}
|
49
cmd/swarm/swarm-snapshot/run_test.go
Normal file
49
cmd/swarm/swarm-snapshot/run_test.go
Normal 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())
|
||||
}
|
@ -57,7 +57,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -140,6 +140,10 @@ var (
|
||||
Name: "rinkeby",
|
||||
Usage: "Rinkeby network: pre-configured proof-of-authority test network",
|
||||
}
|
||||
GoerliFlag = cli.BoolFlag{
|
||||
Name: "goerli",
|
||||
Usage: "Görli network: pre-configured proof-of-authority test network",
|
||||
}
|
||||
ConstantinopleOverrideFlag = cli.Uint64Flag{
|
||||
Name: "override.constantinople",
|
||||
Usage: "Manually specify constantinople fork-block, overriding the bundled setting",
|
||||
@ -614,14 +618,14 @@ var (
|
||||
Usage: "Password to authorize access to the database",
|
||||
Value: "test",
|
||||
}
|
||||
// The `host` tag is part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB.
|
||||
// It is used so that we can group all nodes and average a measurement across all of them, but also so
|
||||
// that we can select a specific node and inspect its measurements.
|
||||
// Tags are part of every measurement sent to InfluxDB. Queries on tags are faster in InfluxDB.
|
||||
// For example `host` tag could be used so that we can group all nodes and average a measurement
|
||||
// across all of them, but also so that we can select a specific node and inspect its measurements.
|
||||
// https://docs.influxdata.com/influxdb/v1.4/concepts/key_concepts/#tag-key
|
||||
MetricsInfluxDBHostTagFlag = cli.StringFlag{
|
||||
Name: "metrics.influxdb.host.tag",
|
||||
Usage: "InfluxDB `host` tag attached to all measurements",
|
||||
Value: "localhost",
|
||||
MetricsInfluxDBTagsFlag = cli.StringFlag{
|
||||
Name: "metrics.influxdb.tags",
|
||||
Usage: "Comma-separated InfluxDB tags (key/values) attached to all measurements",
|
||||
Value: "host=localhost",
|
||||
}
|
||||
|
||||
EWASMInterpreterFlag = cli.StringFlag{
|
||||
@ -647,6 +651,9 @@ func MakeDataDir(ctx *cli.Context) string {
|
||||
if ctx.GlobalBool(RinkebyFlag.Name) {
|
||||
return filepath.Join(path, "rinkeby")
|
||||
}
|
||||
if ctx.GlobalBool(GoerliFlag.Name) {
|
||||
return filepath.Join(path, "goerli")
|
||||
}
|
||||
return path
|
||||
}
|
||||
Fatalf("Cannot determine default data directory, please set manually (--datadir)")
|
||||
@ -701,6 +708,8 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
|
||||
urls = params.TestnetBootnodes
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
urls = params.RinkebyBootnodes
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
urls = params.GoerliBootnodes
|
||||
case cfg.BootstrapNodes != nil:
|
||||
return // already set, don't apply defaults.
|
||||
}
|
||||
@ -728,6 +737,8 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
|
||||
}
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
urls = params.RinkebyBootnodes
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
urls = params.GoerliBootnodes
|
||||
case cfg.BootstrapNodesV5 != nil:
|
||||
return // already set, don't apply defaults.
|
||||
}
|
||||
@ -836,10 +847,11 @@ func makeDatabaseHandles() int {
|
||||
if err != nil {
|
||||
Fatalf("Failed to retrieve file descriptor allowance: %v", err)
|
||||
}
|
||||
if err := fdlimit.Raise(uint64(limit)); err != nil {
|
||||
raised, err := fdlimit.Raise(uint64(limit))
|
||||
if err != nil {
|
||||
Fatalf("Failed to raise file descriptor allowance: %v", err)
|
||||
}
|
||||
return limit / 2 // Leave half for networking and other stuff
|
||||
return int(raised / 2) // Leave half for networking and other stuff
|
||||
}
|
||||
|
||||
// MakeAddress converts an account specified directly as a hex encoded string or
|
||||
@ -980,7 +992,6 @@ func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
||||
setHTTP(ctx, cfg)
|
||||
setWS(ctx, cfg)
|
||||
setNodeUserIdent(ctx, cfg)
|
||||
|
||||
setDataDir(ctx, cfg)
|
||||
|
||||
if ctx.GlobalIsSet(KeyStoreDirFlag.Name) {
|
||||
@ -1004,6 +1015,8 @@ func setDataDir(ctx *cli.Context, cfg *node.Config) {
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "testnet")
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "rinkeby")
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
cfg.DataDir = filepath.Join(node.DefaultDataDir(), "goerli")
|
||||
}
|
||||
}
|
||||
|
||||
@ -1160,7 +1173,7 @@ func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
|
||||
// SetEthConfig applies eth-related command line flags to the config.
|
||||
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||
// Avoid conflicting network flags
|
||||
checkExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag)
|
||||
checkExclusive(ctx, DeveloperFlag, TestnetFlag, RinkebyFlag, GoerliFlag)
|
||||
checkExclusive(ctx, LightServFlag, SyncModeFlag, "light")
|
||||
|
||||
ks := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
|
||||
@ -1256,6 +1269,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||
cfg.NetworkId = 4
|
||||
}
|
||||
cfg.Genesis = core.DefaultRinkebyGenesisBlock()
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 5
|
||||
}
|
||||
cfg.Genesis = core.DefaultGoerliGenesisBlock()
|
||||
case ctx.GlobalBool(DeveloperFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 1337
|
||||
@ -1360,18 +1378,35 @@ func SetupMetrics(ctx *cli.Context) {
|
||||
database = ctx.GlobalString(MetricsInfluxDBDatabaseFlag.Name)
|
||||
username = ctx.GlobalString(MetricsInfluxDBUsernameFlag.Name)
|
||||
password = ctx.GlobalString(MetricsInfluxDBPasswordFlag.Name)
|
||||
hosttag = ctx.GlobalString(MetricsInfluxDBHostTagFlag.Name)
|
||||
)
|
||||
|
||||
if enableExport {
|
||||
tagsMap := SplitTagsFlag(ctx.GlobalString(MetricsInfluxDBTagsFlag.Name))
|
||||
|
||||
log.Info("Enabling metrics export to InfluxDB")
|
||||
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", map[string]string{
|
||||
"host": hosttag,
|
||||
})
|
||||
|
||||
go influxdb.InfluxDBWithTags(metrics.DefaultRegistry, 10*time.Second, endpoint, database, username, password, "geth.", tagsMap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SplitTagsFlag(tagsFlag string) map[string]string {
|
||||
tags := strings.Split(tagsFlag, ",")
|
||||
tagsMap := map[string]string{}
|
||||
|
||||
for _, t := range tags {
|
||||
if t != "" {
|
||||
kv := strings.Split(t, "=")
|
||||
|
||||
if len(kv) == 2 {
|
||||
tagsMap[kv[0]] = kv[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tagsMap
|
||||
}
|
||||
|
||||
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
|
||||
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
|
||||
var (
|
||||
@ -1396,6 +1431,8 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||
genesis = core.DefaultTestnetGenesisBlock()
|
||||
case ctx.GlobalBool(RinkebyFlag.Name):
|
||||
genesis = core.DefaultRinkebyGenesisBlock()
|
||||
case ctx.GlobalBool(GoerliFlag.Name):
|
||||
genesis = core.DefaultGoerliGenesisBlock()
|
||||
case ctx.GlobalBool(DeveloperFlag.Name):
|
||||
Fatalf("Developer chains are ephemeral")
|
||||
}
|
||||
|
64
cmd/utils/flags_test.go
Normal file
64
cmd/utils/flags_test.go
Normal file
@ -0,0 +1,64 @@
|
||||
// Copyright 2019 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 utils contains internal helper functions for go-ethereum commands.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_SplitTagsFlag(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args string
|
||||
want map[string]string
|
||||
}{
|
||||
{
|
||||
"2 tags case",
|
||||
"host=localhost,bzzkey=123",
|
||||
map[string]string{
|
||||
"host": "localhost",
|
||||
"bzzkey": "123",
|
||||
},
|
||||
},
|
||||
{
|
||||
"1 tag case",
|
||||
"host=localhost123",
|
||||
map[string]string{
|
||||
"host": "localhost123",
|
||||
},
|
||||
},
|
||||
{
|
||||
"empty case",
|
||||
"",
|
||||
map[string]string{},
|
||||
},
|
||||
{
|
||||
"garbage",
|
||||
"smth=smthelse=123",
|
||||
map[string]string{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := SplitTagsFlag(tt.args); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("splitTagsFlag() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -26,11 +26,11 @@ import "syscall"
|
||||
|
||||
// Raise tries to maximize the file descriptor allowance of this process
|
||||
// to the maximum hard-limit allowed by the OS.
|
||||
func Raise(max uint64) error {
|
||||
func Raise(max uint64) (uint64, error) {
|
||||
// Get the current limit
|
||||
var limit syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
// Try to update the limit to the max allowance
|
||||
limit.Cur = limit.Max
|
||||
@ -38,9 +38,12 @@ func Raise(max uint64) error {
|
||||
limit.Cur = int64(max)
|
||||
}
|
||||
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
return nil
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return limit.Cur, nil
|
||||
}
|
||||
|
||||
// Current retrieves the number of file descriptors allowed to be opened by this
|
||||
|
@ -36,7 +36,7 @@ func TestFileDescriptorLimits(t *testing.T) {
|
||||
if limit, err := Current(); err != nil || limit <= 0 {
|
||||
t.Fatalf("failed to retrieve file descriptor limit (%d): %v", limit, err)
|
||||
}
|
||||
if err := Raise(uint64(target)); err != nil {
|
||||
if _, err := Raise(uint64(target)); err != nil {
|
||||
t.Fatalf("failed to raise file allowance")
|
||||
}
|
||||
if limit, err := Current(); err != nil || limit < target {
|
||||
|
@ -22,11 +22,12 @@ import "syscall"
|
||||
|
||||
// Raise tries to maximize the file descriptor allowance of this process
|
||||
// to the maximum hard-limit allowed by the OS.
|
||||
func Raise(max uint64) error {
|
||||
// Returns the size it was set to (may differ from the desired 'max')
|
||||
func Raise(max uint64) (uint64, error) {
|
||||
// Get the current limit
|
||||
var limit syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
// Try to update the limit to the max allowance
|
||||
limit.Cur = limit.Max
|
||||
@ -34,9 +35,13 @@ func Raise(max uint64) error {
|
||||
limit.Cur = max
|
||||
}
|
||||
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
return nil
|
||||
// MacOS can silently apply further caps, so retrieve the actually set limit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return limit.Cur, nil
|
||||
}
|
||||
|
||||
// Current retrieves the number of file descriptors allowed to be opened by this
|
||||
|
@ -16,28 +16,30 @@
|
||||
|
||||
package fdlimit
|
||||
|
||||
import "errors"
|
||||
import "fmt"
|
||||
|
||||
const hardlimit = 16384
|
||||
|
||||
// Raise tries to maximize the file descriptor allowance of this process
|
||||
// to the maximum hard-limit allowed by the OS.
|
||||
func Raise(max uint64) error {
|
||||
func Raise(max uint64) (uint64, error) {
|
||||
// This method is NOP by design:
|
||||
// * Linux/Darwin counterparts need to manually increase per process limits
|
||||
// * On Windows Go uses the CreateFile API, which is limited to 16K files, non
|
||||
// changeable from within a running process
|
||||
// This way we can always "request" raising the limits, which will either have
|
||||
// or not have effect based on the platform we're running on.
|
||||
if max > 16384 {
|
||||
return errors.New("file descriptor limit (16384) reached")
|
||||
if max > hardlimit {
|
||||
return hardlimit, fmt.Errorf("file descriptor limit (%d) reached", hardlimit)
|
||||
}
|
||||
return nil
|
||||
return max, nil
|
||||
}
|
||||
|
||||
// Current retrieves the number of file descriptors allowed to be opened by this
|
||||
// process.
|
||||
func Current() (int, error) {
|
||||
// Please see Raise for the reason why we use hard coded 16K as the limit
|
||||
return 16384, nil
|
||||
return hardlimit, nil
|
||||
}
|
||||
|
||||
// Maximum retrieves the maximum number of file descriptors this process is
|
||||
|
@ -27,7 +27,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"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.
|
||||
@ -196,7 +196,7 @@ func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||
// Hex returns an EIP55-compliant hex string representation of the address.
|
||||
func (a Address) Hex() string {
|
||||
unchecksummed := hex.EncodeToString(a[:])
|
||||
sha := sha3.NewKeccak256()
|
||||
sha := sha3.NewLegacyKeccak256()
|
||||
sha.Write([]byte(unchecksummed))
|
||||
hash := sha.Sum(nil)
|
||||
|
||||
|
@ -33,13 +33,13 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
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
|
||||
// or not), which could be abused to produce different hashes for the same header.
|
||||
func sigHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewKeccak256()
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
|
||||
rlp.Encode(hasher, []interface{}{
|
||||
header.ParentHash,
|
||||
|
@ -30,8 +30,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/bitutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -123,7 +123,7 @@ func seedHash(block uint64) []byte {
|
||||
if block < epochLength {
|
||||
return seed
|
||||
}
|
||||
keccak256 := makeHasher(sha3.NewKeccak256())
|
||||
keccak256 := makeHasher(sha3.NewLegacyKeccak256())
|
||||
for i := 0; i < int(block/epochLength); i++ {
|
||||
keccak256(seed, seed)
|
||||
}
|
||||
@ -177,7 +177,7 @@ func generateCache(dest []uint32, epoch uint64, seed []byte) {
|
||||
}
|
||||
}()
|
||||
// Create a hasher to reuse between invocations
|
||||
keccak512 := makeHasher(sha3.NewKeccak512())
|
||||
keccak512 := makeHasher(sha3.NewLegacyKeccak512())
|
||||
|
||||
// Sequentially produce the initial dataset
|
||||
keccak512(cache, seed)
|
||||
@ -301,7 +301,7 @@ func generateDataset(dest []uint32, epoch uint64, cache []uint32) {
|
||||
defer pend.Done()
|
||||
|
||||
// Create a hasher to reuse between invocations
|
||||
keccak512 := makeHasher(sha3.NewKeccak512())
|
||||
keccak512 := makeHasher(sha3.NewLegacyKeccak512())
|
||||
|
||||
// Calculate the data segment this thread should generate
|
||||
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
|
||||
// hash and nonce.
|
||||
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 {
|
||||
rawData := generateDatasetItem(cache, index, keccak512)
|
||||
|
@ -31,9 +31,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/consensus/misc"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"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/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func (ethash *Ethash) SealHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewKeccak256()
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
|
||||
rlp.Encode(hasher, []interface{}{
|
||||
header.ParentHash,
|
||||
|
@ -27,40 +27,40 @@ const Version = "1.0"
|
||||
|
||||
var errNoChequebook = errors.New("no chequebook")
|
||||
|
||||
type Api struct {
|
||||
type API struct {
|
||||
chequebookf func() *Chequebook
|
||||
}
|
||||
|
||||
func NewApi(ch func() *Chequebook) *Api {
|
||||
return &Api{ch}
|
||||
func NewAPI(ch func() *Chequebook) *API {
|
||||
return &API{ch}
|
||||
}
|
||||
|
||||
func (self *Api) Balance() (string, error) {
|
||||
ch := self.chequebookf()
|
||||
func (a *API) Balance() (string, error) {
|
||||
ch := a.chequebookf()
|
||||
if ch == nil {
|
||||
return "", errNoChequebook
|
||||
}
|
||||
return ch.Balance().String(), nil
|
||||
}
|
||||
|
||||
func (self *Api) Issue(beneficiary common.Address, amount *big.Int) (cheque *Cheque, err error) {
|
||||
ch := self.chequebookf()
|
||||
func (a *API) Issue(beneficiary common.Address, amount *big.Int) (cheque *Cheque, err error) {
|
||||
ch := a.chequebookf()
|
||||
if ch == nil {
|
||||
return nil, errNoChequebook
|
||||
}
|
||||
return ch.Issue(beneficiary, amount)
|
||||
}
|
||||
|
||||
func (self *Api) Cash(cheque *Cheque) (txhash string, err error) {
|
||||
ch := self.chequebookf()
|
||||
func (a *API) Cash(cheque *Cheque) (txhash string, err error) {
|
||||
ch := a.chequebookf()
|
||||
if ch == nil {
|
||||
return "", errNoChequebook
|
||||
}
|
||||
return ch.Cash(cheque)
|
||||
}
|
||||
|
||||
func (self *Api) Deposit(amount *big.Int) (txhash string, err error) {
|
||||
ch := self.chequebookf()
|
||||
func (a *API) Deposit(amount *big.Int) (txhash string, err error) {
|
||||
ch := a.chequebookf()
|
||||
if ch == nil {
|
||||
return "", errNoChequebook
|
||||
}
|
||||
|
@ -75,8 +75,8 @@ type Cheque struct {
|
||||
Sig []byte // signature Sign(Keccak256(contract, beneficiary, amount), prvKey)
|
||||
}
|
||||
|
||||
func (self *Cheque) String() string {
|
||||
return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", self.Contract.Hex(), self.Beneficiary.Hex(), self.Amount, self.Sig)
|
||||
func (ch *Cheque) String() string {
|
||||
return fmt.Sprintf("contract: %s, beneficiary: %s, amount: %v, signature: %x", ch.Contract.Hex(), ch.Beneficiary.Hex(), ch.Amount, ch.Sig)
|
||||
}
|
||||
|
||||
type Params struct {
|
||||
@ -109,8 +109,8 @@ type Chequebook struct {
|
||||
log log.Logger // contextual logger with the contract address embedded
|
||||
}
|
||||
|
||||
func (self *Chequebook) String() string {
|
||||
return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", self.contractAddr.Hex(), self.owner.Hex(), self.balance, self.prvKey.PublicKey)
|
||||
func (chbook *Chequebook) String() string {
|
||||
return fmt.Sprintf("contract: %s, owner: %s, balance: %v, signer: %x", chbook.contractAddr.Hex(), chbook.owner.Hex(), chbook.balance, chbook.prvKey.PublicKey)
|
||||
}
|
||||
|
||||
// NewChequebook creates a new Chequebook.
|
||||
@ -148,12 +148,12 @@ func NewChequebook(path string, contractAddr common.Address, prvKey *ecdsa.Priva
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Chequebook) setBalanceFromBlockChain() {
|
||||
balance, err := self.backend.BalanceAt(context.TODO(), self.contractAddr, nil)
|
||||
func (chbook *Chequebook) setBalanceFromBlockChain() {
|
||||
balance, err := chbook.backend.BalanceAt(context.TODO(), chbook.contractAddr, nil)
|
||||
if err != nil {
|
||||
log.Error("Failed to retrieve chequebook balance", "err", err)
|
||||
} else {
|
||||
self.balance.Set(balance)
|
||||
chbook.balance.Set(balance)
|
||||
}
|
||||
}
|
||||
|
||||
@ -187,19 +187,19 @@ type chequebookFile struct {
|
||||
}
|
||||
|
||||
// UnmarshalJSON deserialises a chequebook.
|
||||
func (self *Chequebook) UnmarshalJSON(data []byte) error {
|
||||
func (chbook *Chequebook) UnmarshalJSON(data []byte) error {
|
||||
var file chequebookFile
|
||||
err := json.Unmarshal(data, &file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, ok := self.balance.SetString(file.Balance, 10)
|
||||
_, ok := chbook.balance.SetString(file.Balance, 10)
|
||||
if !ok {
|
||||
return fmt.Errorf("cumulative amount sent: unable to convert string to big integer: %v", file.Balance)
|
||||
}
|
||||
self.contractAddr = common.HexToAddress(file.Contract)
|
||||
chbook.contractAddr = common.HexToAddress(file.Contract)
|
||||
for addr, sent := range file.Sent {
|
||||
self.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10)
|
||||
chbook.sent[common.HexToAddress(addr)], ok = new(big.Int).SetString(sent, 10)
|
||||
if !ok {
|
||||
return fmt.Errorf("beneficiary %v cumulative amount sent: unable to convert string to big integer: %v", addr, sent)
|
||||
}
|
||||
@ -208,14 +208,14 @@ func (self *Chequebook) UnmarshalJSON(data []byte) error {
|
||||
}
|
||||
|
||||
// MarshalJSON serialises a chequebook.
|
||||
func (self *Chequebook) MarshalJSON() ([]byte, error) {
|
||||
func (chbook *Chequebook) MarshalJSON() ([]byte, error) {
|
||||
var file = &chequebookFile{
|
||||
Balance: self.balance.String(),
|
||||
Contract: self.contractAddr.Hex(),
|
||||
Owner: self.owner.Hex(),
|
||||
Balance: chbook.balance.String(),
|
||||
Contract: chbook.contractAddr.Hex(),
|
||||
Owner: chbook.owner.Hex(),
|
||||
Sent: make(map[string]string),
|
||||
}
|
||||
for addr, sent := range self.sent {
|
||||
for addr, sent := range chbook.sent {
|
||||
file.Sent[addr.Hex()] = sent.String()
|
||||
}
|
||||
return json.Marshal(file)
|
||||
@ -223,67 +223,67 @@ func (self *Chequebook) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// Save persists the chequebook on disk, remembering balance, contract address and
|
||||
// cumulative amount of funds sent for each beneficiary.
|
||||
func (self *Chequebook) Save() (err error) {
|
||||
data, err := json.MarshalIndent(self, "", " ")
|
||||
func (chbook *Chequebook) Save() (err error) {
|
||||
data, err := json.MarshalIndent(chbook, "", " ")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
self.log.Trace("Saving chequebook to disk", self.path)
|
||||
chbook.log.Trace("Saving chequebook to disk", chbook.path)
|
||||
|
||||
return ioutil.WriteFile(self.path, data, os.ModePerm)
|
||||
return ioutil.WriteFile(chbook.path, data, os.ModePerm)
|
||||
}
|
||||
|
||||
// Stop quits the autodeposit go routine to terminate
|
||||
func (self *Chequebook) Stop() {
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
if self.quit != nil {
|
||||
close(self.quit)
|
||||
self.quit = nil
|
||||
func (chbook *Chequebook) Stop() {
|
||||
defer chbook.lock.Unlock()
|
||||
chbook.lock.Lock()
|
||||
if chbook.quit != nil {
|
||||
close(chbook.quit)
|
||||
chbook.quit = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Issue creates a cheque signed by the chequebook owner's private key. The
|
||||
// signer commits to a contract (one that they own), a beneficiary and amount.
|
||||
func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
func (chbook *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *Cheque, err error) {
|
||||
defer chbook.lock.Unlock()
|
||||
chbook.lock.Lock()
|
||||
|
||||
if amount.Sign() <= 0 {
|
||||
return nil, fmt.Errorf("amount must be greater than zero (%v)", amount)
|
||||
}
|
||||
if self.balance.Cmp(amount) < 0 {
|
||||
err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, self.balance)
|
||||
if chbook.balance.Cmp(amount) < 0 {
|
||||
err = fmt.Errorf("insufficient funds to issue cheque for amount: %v. balance: %v", amount, chbook.balance)
|
||||
} else {
|
||||
var sig []byte
|
||||
sent, found := self.sent[beneficiary]
|
||||
sent, found := chbook.sent[beneficiary]
|
||||
if !found {
|
||||
sent = new(big.Int)
|
||||
self.sent[beneficiary] = sent
|
||||
chbook.sent[beneficiary] = sent
|
||||
}
|
||||
sum := new(big.Int).Set(sent)
|
||||
sum.Add(sum, amount)
|
||||
|
||||
sig, err = crypto.Sign(sigHash(self.contractAddr, beneficiary, sum), self.prvKey)
|
||||
sig, err = crypto.Sign(sigHash(chbook.contractAddr, beneficiary, sum), chbook.prvKey)
|
||||
if err == nil {
|
||||
ch = &Cheque{
|
||||
Contract: self.contractAddr,
|
||||
Contract: chbook.contractAddr,
|
||||
Beneficiary: beneficiary,
|
||||
Amount: sum,
|
||||
Sig: sig,
|
||||
}
|
||||
sent.Set(sum)
|
||||
self.balance.Sub(self.balance, amount) // subtract amount from balance
|
||||
chbook.balance.Sub(chbook.balance, amount) // subtract amount from balance
|
||||
}
|
||||
}
|
||||
|
||||
// auto deposit if threshold is set and balance is less then threshold
|
||||
// note this is called even if issuing cheque fails
|
||||
// so we reattempt depositing
|
||||
if self.threshold != nil {
|
||||
if self.balance.Cmp(self.threshold) < 0 {
|
||||
send := new(big.Int).Sub(self.buffer, self.balance)
|
||||
self.deposit(send)
|
||||
if chbook.threshold != nil {
|
||||
if chbook.balance.Cmp(chbook.threshold) < 0 {
|
||||
send := new(big.Int).Sub(chbook.buffer, chbook.balance)
|
||||
chbook.deposit(send)
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,8 +291,8 @@ func (self *Chequebook) Issue(beneficiary common.Address, amount *big.Int) (ch *
|
||||
}
|
||||
|
||||
// Cash is a convenience method to cash any cheque.
|
||||
func (self *Chequebook) Cash(ch *Cheque) (txhash string, err error) {
|
||||
return ch.Cash(self.session)
|
||||
func (chbook *Chequebook) Cash(ch *Cheque) (txhash string, err error) {
|
||||
return ch.Cash(chbook.session)
|
||||
}
|
||||
|
||||
// data to sign: contract address, beneficiary, cumulative amount of funds ever sent
|
||||
@ -309,73 +309,73 @@ func sigHash(contract, beneficiary common.Address, sum *big.Int) []byte {
|
||||
}
|
||||
|
||||
// Balance returns the current balance of the chequebook.
|
||||
func (self *Chequebook) Balance() *big.Int {
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
return new(big.Int).Set(self.balance)
|
||||
func (chbook *Chequebook) Balance() *big.Int {
|
||||
defer chbook.lock.Unlock()
|
||||
chbook.lock.Lock()
|
||||
return new(big.Int).Set(chbook.balance)
|
||||
}
|
||||
|
||||
// Owner returns the owner account of the chequebook.
|
||||
func (self *Chequebook) Owner() common.Address {
|
||||
return self.owner
|
||||
func (chbook *Chequebook) Owner() common.Address {
|
||||
return chbook.owner
|
||||
}
|
||||
|
||||
// Address returns the on-chain contract address of the chequebook.
|
||||
func (self *Chequebook) Address() common.Address {
|
||||
return self.contractAddr
|
||||
func (chbook *Chequebook) Address() common.Address {
|
||||
return chbook.contractAddr
|
||||
}
|
||||
|
||||
// Deposit deposits money to the chequebook account.
|
||||
func (self *Chequebook) Deposit(amount *big.Int) (string, error) {
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
return self.deposit(amount)
|
||||
func (chbook *Chequebook) Deposit(amount *big.Int) (string, error) {
|
||||
defer chbook.lock.Unlock()
|
||||
chbook.lock.Lock()
|
||||
return chbook.deposit(amount)
|
||||
}
|
||||
|
||||
// deposit deposits amount to the chequebook account.
|
||||
// The caller must hold self.lock.
|
||||
func (self *Chequebook) deposit(amount *big.Int) (string, error) {
|
||||
func (chbook *Chequebook) deposit(amount *big.Int) (string, error) {
|
||||
// since the amount is variable here, we do not use sessions
|
||||
depositTransactor := bind.NewKeyedTransactor(self.prvKey)
|
||||
depositTransactor := bind.NewKeyedTransactor(chbook.prvKey)
|
||||
depositTransactor.Value = amount
|
||||
chbookRaw := &contract.ChequebookRaw{Contract: self.contract}
|
||||
chbookRaw := &contract.ChequebookRaw{Contract: chbook.contract}
|
||||
tx, err := chbookRaw.Transfer(depositTransactor)
|
||||
if err != nil {
|
||||
self.log.Warn("Failed to fund chequebook", "amount", amount, "balance", self.balance, "target", self.buffer, "err", err)
|
||||
chbook.log.Warn("Failed to fund chequebook", "amount", amount, "balance", chbook.balance, "target", chbook.buffer, "err", err)
|
||||
return "", err
|
||||
}
|
||||
// assume that transaction is actually successful, we add the amount to balance right away
|
||||
self.balance.Add(self.balance, amount)
|
||||
self.log.Trace("Deposited funds to chequebook", "amount", amount, "balance", self.balance, "target", self.buffer)
|
||||
chbook.balance.Add(chbook.balance, amount)
|
||||
chbook.log.Trace("Deposited funds to chequebook", "amount", amount, "balance", chbook.balance, "target", chbook.buffer)
|
||||
return tx.Hash().Hex(), nil
|
||||
}
|
||||
|
||||
// AutoDeposit (re)sets interval time and amount which triggers sending funds to the
|
||||
// chequebook. Contract backend needs to be set if threshold is not less than buffer, then
|
||||
// deposit will be triggered on every new cheque issued.
|
||||
func (self *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
self.threshold = threshold
|
||||
self.buffer = buffer
|
||||
self.autoDeposit(interval)
|
||||
func (chbook *Chequebook) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
|
||||
defer chbook.lock.Unlock()
|
||||
chbook.lock.Lock()
|
||||
chbook.threshold = threshold
|
||||
chbook.buffer = buffer
|
||||
chbook.autoDeposit(interval)
|
||||
}
|
||||
|
||||
// autoDeposit starts a goroutine that periodically sends funds to the chequebook
|
||||
// contract caller holds the lock the go routine terminates if Chequebook.quit is closed.
|
||||
func (self *Chequebook) autoDeposit(interval time.Duration) {
|
||||
if self.quit != nil {
|
||||
close(self.quit)
|
||||
self.quit = nil
|
||||
func (chbook *Chequebook) autoDeposit(interval time.Duration) {
|
||||
if chbook.quit != nil {
|
||||
close(chbook.quit)
|
||||
chbook.quit = nil
|
||||
}
|
||||
// if threshold >= balance autodeposit after every cheque issued
|
||||
if interval == time.Duration(0) || self.threshold != nil && self.buffer != nil && self.threshold.Cmp(self.buffer) >= 0 {
|
||||
if interval == time.Duration(0) || chbook.threshold != nil && chbook.buffer != nil && chbook.threshold.Cmp(chbook.buffer) >= 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
self.quit = make(chan bool)
|
||||
quit := self.quit
|
||||
chbook.quit = make(chan bool)
|
||||
quit := chbook.quit
|
||||
|
||||
go func() {
|
||||
for {
|
||||
@ -383,15 +383,15 @@ func (self *Chequebook) autoDeposit(interval time.Duration) {
|
||||
case <-quit:
|
||||
return
|
||||
case <-ticker.C:
|
||||
self.lock.Lock()
|
||||
if self.balance.Cmp(self.buffer) < 0 {
|
||||
amount := new(big.Int).Sub(self.buffer, self.balance)
|
||||
txhash, err := self.deposit(amount)
|
||||
chbook.lock.Lock()
|
||||
if chbook.balance.Cmp(chbook.buffer) < 0 {
|
||||
amount := new(big.Int).Sub(chbook.buffer, chbook.balance)
|
||||
txhash, err := chbook.deposit(amount)
|
||||
if err == nil {
|
||||
self.txhash = txhash
|
||||
chbook.txhash = txhash
|
||||
}
|
||||
}
|
||||
self.lock.Unlock()
|
||||
chbook.lock.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -409,21 +409,21 @@ func NewOutbox(chbook *Chequebook, beneficiary common.Address) *Outbox {
|
||||
}
|
||||
|
||||
// Issue creates cheque.
|
||||
func (self *Outbox) Issue(amount *big.Int) (swap.Promise, error) {
|
||||
return self.chequeBook.Issue(self.beneficiary, amount)
|
||||
func (o *Outbox) Issue(amount *big.Int) (swap.Promise, error) {
|
||||
return o.chequeBook.Issue(o.beneficiary, amount)
|
||||
}
|
||||
|
||||
// AutoDeposit enables auto-deposits on the underlying chequebook.
|
||||
func (self *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
|
||||
self.chequeBook.AutoDeposit(interval, threshold, buffer)
|
||||
func (o *Outbox) AutoDeposit(interval time.Duration, threshold, buffer *big.Int) {
|
||||
o.chequeBook.AutoDeposit(interval, threshold, buffer)
|
||||
}
|
||||
|
||||
// Stop helps satisfy the swap.OutPayment interface.
|
||||
func (self *Outbox) Stop() {}
|
||||
func (o *Outbox) Stop() {}
|
||||
|
||||
// String implements fmt.Stringer.
|
||||
func (self *Outbox) String() string {
|
||||
return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.chequeBook.Address().Hex(), self.beneficiary.Hex(), self.chequeBook.Balance())
|
||||
func (o *Outbox) String() string {
|
||||
return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", o.chequeBook.Address().Hex(), o.beneficiary.Hex(), o.chequeBook.Balance())
|
||||
}
|
||||
|
||||
// Inbox can deposit, verify and cash cheques from a single contract to a single
|
||||
@ -474,55 +474,55 @@ func NewInbox(prvKey *ecdsa.PrivateKey, contractAddr, beneficiary common.Address
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Inbox) String() string {
|
||||
return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", self.contract.Hex(), self.beneficiary.Hex(), self.cheque.Amount)
|
||||
func (i *Inbox) String() string {
|
||||
return fmt.Sprintf("chequebook: %v, beneficiary: %s, balance: %v", i.contract.Hex(), i.beneficiary.Hex(), i.cheque.Amount)
|
||||
}
|
||||
|
||||
// Stop quits the autocash goroutine.
|
||||
func (self *Inbox) Stop() {
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
if self.quit != nil {
|
||||
close(self.quit)
|
||||
self.quit = nil
|
||||
func (i *Inbox) Stop() {
|
||||
defer i.lock.Unlock()
|
||||
i.lock.Lock()
|
||||
if i.quit != nil {
|
||||
close(i.quit)
|
||||
i.quit = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Cash attempts to cash the current cheque.
|
||||
func (self *Inbox) Cash() (txhash string, err error) {
|
||||
if self.cheque != nil {
|
||||
txhash, err = self.cheque.Cash(self.session)
|
||||
self.log.Trace("Cashing in chequebook cheque", "amount", self.cheque.Amount, "beneficiary", self.beneficiary)
|
||||
self.cashed = self.cheque.Amount
|
||||
func (i *Inbox) Cash() (txhash string, err error) {
|
||||
if i.cheque != nil {
|
||||
txhash, err = i.cheque.Cash(i.session)
|
||||
i.log.Trace("Cashing in chequebook cheque", "amount", i.cheque.Amount, "beneficiary", i.beneficiary)
|
||||
i.cashed = i.cheque.Amount
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// AutoCash (re)sets maximum time and amount which triggers cashing of the last uncashed
|
||||
// cheque if maxUncashed is set to 0, then autocash on receipt.
|
||||
func (self *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) {
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
self.maxUncashed = maxUncashed
|
||||
self.autoCash(cashInterval)
|
||||
func (i *Inbox) AutoCash(cashInterval time.Duration, maxUncashed *big.Int) {
|
||||
defer i.lock.Unlock()
|
||||
i.lock.Lock()
|
||||
i.maxUncashed = maxUncashed
|
||||
i.autoCash(cashInterval)
|
||||
}
|
||||
|
||||
// autoCash starts a loop that periodically clears the last cheque
|
||||
// if the peer is trusted. Clearing period could be 24h or a week.
|
||||
// The caller must hold self.lock.
|
||||
func (self *Inbox) autoCash(cashInterval time.Duration) {
|
||||
if self.quit != nil {
|
||||
close(self.quit)
|
||||
self.quit = nil
|
||||
func (i *Inbox) autoCash(cashInterval time.Duration) {
|
||||
if i.quit != nil {
|
||||
close(i.quit)
|
||||
i.quit = nil
|
||||
}
|
||||
// if maxUncashed is set to 0, then autocash on receipt
|
||||
if cashInterval == time.Duration(0) || self.maxUncashed != nil && self.maxUncashed.Sign() == 0 {
|
||||
if cashInterval == time.Duration(0) || i.maxUncashed != nil && i.maxUncashed.Sign() == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(cashInterval)
|
||||
self.quit = make(chan bool)
|
||||
quit := self.quit
|
||||
i.quit = make(chan bool)
|
||||
quit := i.quit
|
||||
|
||||
go func() {
|
||||
for {
|
||||
@ -530,14 +530,14 @@ func (self *Inbox) autoCash(cashInterval time.Duration) {
|
||||
case <-quit:
|
||||
return
|
||||
case <-ticker.C:
|
||||
self.lock.Lock()
|
||||
if self.cheque != nil && self.cheque.Amount.Cmp(self.cashed) != 0 {
|
||||
txhash, err := self.Cash()
|
||||
i.lock.Lock()
|
||||
if i.cheque != nil && i.cheque.Amount.Cmp(i.cashed) != 0 {
|
||||
txhash, err := i.Cash()
|
||||
if err == nil {
|
||||
self.txhash = txhash
|
||||
i.txhash = txhash
|
||||
}
|
||||
}
|
||||
self.lock.Unlock()
|
||||
i.lock.Unlock()
|
||||
}
|
||||
}
|
||||
}()
|
||||
@ -545,56 +545,56 @@ func (self *Inbox) autoCash(cashInterval time.Duration) {
|
||||
|
||||
// Receive is called to deposit the latest cheque to the incoming Inbox.
|
||||
// The given promise must be a *Cheque.
|
||||
func (self *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
||||
func (i *Inbox) Receive(promise swap.Promise) (*big.Int, error) {
|
||||
ch := promise.(*Cheque)
|
||||
|
||||
defer self.lock.Unlock()
|
||||
self.lock.Lock()
|
||||
defer i.lock.Unlock()
|
||||
i.lock.Lock()
|
||||
|
||||
var sum *big.Int
|
||||
if self.cheque == nil {
|
||||
if i.cheque == nil {
|
||||
// the sum is checked against the blockchain once a cheque is received
|
||||
tally, err := self.session.Sent(self.beneficiary)
|
||||
tally, err := i.session.Sent(i.beneficiary)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("inbox: error calling backend to set amount: %v", err)
|
||||
}
|
||||
sum = tally
|
||||
} else {
|
||||
sum = self.cheque.Amount
|
||||
sum = i.cheque.Amount
|
||||
}
|
||||
|
||||
amount, err := ch.Verify(self.signer, self.contract, self.beneficiary, sum)
|
||||
amount, err := ch.Verify(i.signer, i.contract, i.beneficiary, sum)
|
||||
var uncashed *big.Int
|
||||
if err == nil {
|
||||
self.cheque = ch
|
||||
i.cheque = ch
|
||||
|
||||
if self.maxUncashed != nil {
|
||||
uncashed = new(big.Int).Sub(ch.Amount, self.cashed)
|
||||
if self.maxUncashed.Cmp(uncashed) < 0 {
|
||||
self.Cash()
|
||||
if i.maxUncashed != nil {
|
||||
uncashed = new(big.Int).Sub(ch.Amount, i.cashed)
|
||||
if i.maxUncashed.Cmp(uncashed) < 0 {
|
||||
i.Cash()
|
||||
}
|
||||
}
|
||||
self.log.Trace("Received cheque in chequebook inbox", "amount", amount, "uncashed", uncashed)
|
||||
i.log.Trace("Received cheque in chequebook inbox", "amount", amount, "uncashed", uncashed)
|
||||
}
|
||||
|
||||
return amount, err
|
||||
}
|
||||
|
||||
// Verify verifies cheque for signer, contract, beneficiary, amount, valid signature.
|
||||
func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
|
||||
log.Trace("Verifying chequebook cheque", "cheque", self, "sum", sum)
|
||||
func (ch *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary common.Address, sum *big.Int) (*big.Int, error) {
|
||||
log.Trace("Verifying chequebook cheque", "cheque", ch, "sum", sum)
|
||||
if sum == nil {
|
||||
return nil, fmt.Errorf("invalid amount")
|
||||
}
|
||||
|
||||
if self.Beneficiary != beneficiary {
|
||||
return nil, fmt.Errorf("beneficiary mismatch: %v != %v", self.Beneficiary.Hex(), beneficiary.Hex())
|
||||
if ch.Beneficiary != beneficiary {
|
||||
return nil, fmt.Errorf("beneficiary mismatch: %v != %v", ch.Beneficiary.Hex(), beneficiary.Hex())
|
||||
}
|
||||
if self.Contract != contract {
|
||||
return nil, fmt.Errorf("contract mismatch: %v != %v", self.Contract.Hex(), contract.Hex())
|
||||
if ch.Contract != contract {
|
||||
return nil, fmt.Errorf("contract mismatch: %v != %v", ch.Contract.Hex(), contract.Hex())
|
||||
}
|
||||
|
||||
amount := new(big.Int).Set(self.Amount)
|
||||
amount := new(big.Int).Set(ch.Amount)
|
||||
if sum != nil {
|
||||
amount.Sub(amount, sum)
|
||||
if amount.Sign() <= 0 {
|
||||
@ -602,7 +602,7 @@ func (self *Cheque) Verify(signerKey *ecdsa.PublicKey, contract, beneficiary com
|
||||
}
|
||||
}
|
||||
|
||||
pubKey, err := crypto.SigToPub(sigHash(self.Contract, beneficiary, self.Amount), self.Sig)
|
||||
pubKey, err := crypto.SigToPub(sigHash(ch.Contract, beneficiary, ch.Amount), ch.Sig)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid signature: %v", err)
|
||||
}
|
||||
@ -621,9 +621,9 @@ func sig2vrs(sig []byte) (v byte, r, s [32]byte) {
|
||||
}
|
||||
|
||||
// Cash cashes the cheque by sending an Ethereum transaction.
|
||||
func (self *Cheque) Cash(session *contract.ChequebookSession) (string, error) {
|
||||
v, r, s := sig2vrs(self.Sig)
|
||||
tx, err := session.Cash(self.Beneficiary, self.Amount, v, r, s)
|
||||
func (ch *Cheque) Cash(session *contract.ChequebookSession) (string, error) {
|
||||
v, r, s := sig2vrs(ch.Sig)
|
||||
tx, err := session.Cash(ch.Beneficiary, ch.Amount, v, r, s)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
@ -205,22 +205,22 @@ func (_Chequebook *ChequebookCallerSession) Sent(arg0 common.Address) (*big.Int,
|
||||
// Cash is a paid mutator transaction binding the contract method 0xfbf788d6.
|
||||
//
|
||||
// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns()
|
||||
func (_Chequebook *ChequebookTransactor) Cash(opts *bind.TransactOpts, beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) {
|
||||
return _Chequebook.contract.Transact(opts, "cash", beneficiary, amount, sig_v, sig_r, sig_s)
|
||||
func (_Chequebook *ChequebookTransactor) Cash(opts *bind.TransactOpts, beneficiary common.Address, amount *big.Int, sigV uint8, sigR [32]byte, sigS [32]byte) (*types.Transaction, error) {
|
||||
return _Chequebook.contract.Transact(opts, "cash", beneficiary, amount, sigV, sigR, sigS)
|
||||
}
|
||||
|
||||
// Cash is a paid mutator transaction binding the contract method 0xfbf788d6.
|
||||
//
|
||||
// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns()
|
||||
func (_Chequebook *ChequebookSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) {
|
||||
return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s)
|
||||
func (_Chequebook *ChequebookSession) Cash(beneficiary common.Address, amount *big.Int, sigV uint8, sigR [32]byte, sigS [32]byte) (*types.Transaction, error) {
|
||||
return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sigV, sigR, sigS)
|
||||
}
|
||||
|
||||
// Cash is a paid mutator transaction binding the contract method 0xfbf788d6.
|
||||
//
|
||||
// Solidity: function cash(beneficiary address, amount uint256, sig_v uint8, sig_r bytes32, sig_s bytes32) returns()
|
||||
func (_Chequebook *ChequebookTransactorSession) Cash(beneficiary common.Address, amount *big.Int, sig_v uint8, sig_r [32]byte, sig_s [32]byte) (*types.Transaction, error) {
|
||||
return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sig_v, sig_r, sig_s)
|
||||
func (_Chequebook *ChequebookTransactorSession) Cash(beneficiary common.Address, amount *big.Int, sigV uint8, sigR [32]byte, sigS [32]byte) (*types.Transaction, error) {
|
||||
return _Chequebook.Contract.Cash(&_Chequebook.TransactOpts, beneficiary, amount, sigV, sigR, sigS)
|
||||
}
|
||||
|
||||
// Kill is a paid mutator transaction binding the contract method 0x41c0e1b5.
|
||||
|
@ -227,10 +227,10 @@ func (_ENS *ENSCallerSession) Resolver(node [32]byte) (common.Address, error) {
|
||||
return _ENS.Contract.Resolver(&_ENS.CallOpts, node)
|
||||
}
|
||||
|
||||
// Ttl is a free data retrieval call binding the contract method 0x16a25cbd.
|
||||
// TTL is a free data retrieval call binding the contract method 0x16a25cbd.
|
||||
//
|
||||
// Solidity: function ttl(node bytes32) constant returns(uint64)
|
||||
func (_ENS *ENSCaller) Ttl(opts *bind.CallOpts, node [32]byte) (uint64, error) {
|
||||
func (_ENS *ENSCaller) TTL(opts *bind.CallOpts, node [32]byte) (uint64, error) {
|
||||
var (
|
||||
ret0 = new(uint64)
|
||||
)
|
||||
@ -239,18 +239,18 @@ func (_ENS *ENSCaller) Ttl(opts *bind.CallOpts, node [32]byte) (uint64, error) {
|
||||
return *ret0, err
|
||||
}
|
||||
|
||||
// Ttl is a free data retrieval call binding the contract method 0x16a25cbd.
|
||||
// TTL is a free data retrieval call binding the contract method 0x16a25cbd.
|
||||
//
|
||||
// Solidity: function ttl(node bytes32) constant returns(uint64)
|
||||
func (_ENS *ENSSession) Ttl(node [32]byte) (uint64, error) {
|
||||
return _ENS.Contract.Ttl(&_ENS.CallOpts, node)
|
||||
func (_ENS *ENSSession) TTL(node [32]byte) (uint64, error) {
|
||||
return _ENS.Contract.TTL(&_ENS.CallOpts, node)
|
||||
}
|
||||
|
||||
// Ttl is a free data retrieval call binding the contract method 0x16a25cbd.
|
||||
// TTL is a free data retrieval call binding the contract method 0x16a25cbd.
|
||||
//
|
||||
// Solidity: function ttl(node bytes32) constant returns(uint64)
|
||||
func (_ENS *ENSCallerSession) Ttl(node [32]byte) (uint64, error) {
|
||||
return _ENS.Contract.Ttl(&_ENS.CallOpts, node)
|
||||
func (_ENS *ENSCallerSession) TTL(node [32]byte) (uint64, error) {
|
||||
return _ENS.Contract.TTL(&_ENS.CallOpts, node)
|
||||
}
|
||||
|
||||
// SetOwner is a paid mutator transaction binding the contract method 0x5b0fc9c3.
|
||||
@ -682,7 +682,7 @@ func (it *ENSNewTTLIterator) Close() error {
|
||||
// ENSNewTTL represents a NewTTL event raised by the ENS contract.
|
||||
type ENSNewTTL struct {
|
||||
Node [32]byte
|
||||
Ttl uint64
|
||||
TTL uint64
|
||||
Raw types.Log // Blockchain specific contextual infos
|
||||
}
|
||||
|
||||
|
@ -35,7 +35,7 @@ var (
|
||||
TestNetAddress = common.HexToAddress("0x112234455c3a32fd11230c42e7bccd4a84e02010")
|
||||
)
|
||||
|
||||
// swarm domain name registry and resolver
|
||||
// ENS is the swarm domain name registry and resolver
|
||||
type ENS struct {
|
||||
*contract.ENSSession
|
||||
contractBackend bind.ContractBackend
|
||||
@ -48,7 +48,6 @@ func NewENS(transactOpts *bind.TransactOpts, contractAddr common.Address, contra
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &ENS{
|
||||
&contract.ENSSession{
|
||||
Contract: ens,
|
||||
@ -60,27 +59,24 @@ func NewENS(transactOpts *bind.TransactOpts, contractAddr common.Address, contra
|
||||
|
||||
// DeployENS deploys an instance of the ENS nameservice, with a 'first-in, first-served' root registrar.
|
||||
func DeployENS(transactOpts *bind.TransactOpts, contractBackend bind.ContractBackend) (common.Address, *ENS, error) {
|
||||
// Deploy the ENS registry.
|
||||
// Deploy the ENS registry
|
||||
ensAddr, _, _, err := contract.DeployENS(transactOpts, contractBackend)
|
||||
if err != nil {
|
||||
return ensAddr, nil, err
|
||||
}
|
||||
|
||||
ens, err := NewENS(transactOpts, ensAddr, contractBackend)
|
||||
if err != nil {
|
||||
return ensAddr, nil, err
|
||||
}
|
||||
|
||||
// Deploy the registrar.
|
||||
// Deploy the registrar
|
||||
regAddr, _, _, err := contract.DeployFIFSRegistrar(transactOpts, contractBackend, ensAddr, [32]byte{})
|
||||
if err != nil {
|
||||
return ensAddr, nil, err
|
||||
}
|
||||
// Set the registrar as owner of the ENS root.
|
||||
// Set the registrar as owner of the ENS root
|
||||
if _, err = ens.SetOwner([32]byte{}, regAddr); err != nil {
|
||||
return ensAddr, nil, err
|
||||
}
|
||||
|
||||
return ensAddr, ens, nil
|
||||
}
|
||||
|
||||
@ -89,10 +85,9 @@ func ensParentNode(name string) (common.Hash, common.Hash) {
|
||||
label := crypto.Keccak256Hash([]byte(parts[0]))
|
||||
if len(parts) == 1 {
|
||||
return [32]byte{}, label
|
||||
} else {
|
||||
parentNode, parentLabel := ensParentNode(parts[1])
|
||||
return crypto.Keccak256Hash(parentNode[:], parentLabel[:]), label
|
||||
}
|
||||
parentNode, parentLabel := ensParentNode(parts[1])
|
||||
return crypto.Keccak256Hash(parentNode[:], parentLabel[:]), label
|
||||
}
|
||||
|
||||
func EnsNode(name string) common.Hash {
|
||||
@ -100,111 +95,101 @@ func EnsNode(name string) common.Hash {
|
||||
return crypto.Keccak256Hash(parentNode[:], parentLabel[:])
|
||||
}
|
||||
|
||||
func (self *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, error) {
|
||||
resolverAddr, err := self.Resolver(node)
|
||||
func (ens *ENS) getResolver(node [32]byte) (*contract.PublicResolverSession, error) {
|
||||
resolverAddr, err := ens.Resolver(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resolver, err := contract.NewPublicResolver(resolverAddr, self.contractBackend)
|
||||
resolver, err := contract.NewPublicResolver(resolverAddr, ens.contractBackend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &contract.PublicResolverSession{
|
||||
Contract: resolver,
|
||||
TransactOpts: self.TransactOpts,
|
||||
TransactOpts: ens.TransactOpts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (self *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) {
|
||||
registrarAddr, err := self.Owner(node)
|
||||
func (ens *ENS) getRegistrar(node [32]byte) (*contract.FIFSRegistrarSession, error) {
|
||||
registrarAddr, err := ens.Owner(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registrar, err := contract.NewFIFSRegistrar(registrarAddr, self.contractBackend)
|
||||
registrar, err := contract.NewFIFSRegistrar(registrarAddr, ens.contractBackend)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &contract.FIFSRegistrarSession{
|
||||
Contract: registrar,
|
||||
TransactOpts: self.TransactOpts,
|
||||
TransactOpts: ens.TransactOpts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Resolve is a non-transactional call that returns the content hash associated with a name.
|
||||
func (self *ENS) Resolve(name string) (common.Hash, error) {
|
||||
func (ens *ENS) Resolve(name string) (common.Hash, error) {
|
||||
node := EnsNode(name)
|
||||
|
||||
resolver, err := self.getResolver(node)
|
||||
resolver, err := ens.getResolver(node)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
ret, err := resolver.Content(node)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
return common.BytesToHash(ret[:]), nil
|
||||
}
|
||||
|
||||
// Addr is a non-transactional call that returns the address associated with a name.
|
||||
func (self *ENS) Addr(name string) (common.Address, error) {
|
||||
func (ens *ENS) Addr(name string) (common.Address, error) {
|
||||
node := EnsNode(name)
|
||||
|
||||
resolver, err := self.getResolver(node)
|
||||
resolver, err := ens.getResolver(node)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
|
||||
ret, err := resolver.Addr(node)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
|
||||
return common.BytesToAddress(ret[:]), nil
|
||||
}
|
||||
|
||||
// SetAddress sets the address associated with a name. Only works if the caller
|
||||
// owns the name, and the associated resolver implements a `setAddress` function.
|
||||
func (self *ENS) SetAddr(name string, addr common.Address) (*types.Transaction, error) {
|
||||
func (ens *ENS) SetAddr(name string, addr common.Address) (*types.Transaction, error) {
|
||||
node := EnsNode(name)
|
||||
|
||||
resolver, err := self.getResolver(node)
|
||||
resolver, err := ens.getResolver(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := self.TransactOpts
|
||||
opts := ens.TransactOpts
|
||||
opts.GasLimit = 200000
|
||||
return resolver.Contract.SetAddr(&opts, node, addr)
|
||||
}
|
||||
|
||||
// Register registers a new domain name for the caller, making them the owner of the new name.
|
||||
// Only works if the registrar for the parent domain implements the FIFS registrar protocol.
|
||||
func (self *ENS) Register(name string) (*types.Transaction, error) {
|
||||
func (ens *ENS) Register(name string) (*types.Transaction, error) {
|
||||
parentNode, label := ensParentNode(name)
|
||||
registrar, err := self.getRegistrar(parentNode)
|
||||
registrar, err := ens.getRegistrar(parentNode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return registrar.Contract.Register(&self.TransactOpts, label, self.TransactOpts.From)
|
||||
return registrar.Contract.Register(&ens.TransactOpts, label, ens.TransactOpts.From)
|
||||
}
|
||||
|
||||
// SetContentHash sets the content hash associated with a name. Only works if the caller
|
||||
// owns the name, and the associated resolver implements a `setContent` function.
|
||||
func (self *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) {
|
||||
func (ens *ENS) SetContentHash(name string, hash common.Hash) (*types.Transaction, error) {
|
||||
node := EnsNode(name)
|
||||
|
||||
resolver, err := self.getResolver(node)
|
||||
resolver, err := ens.getResolver(node)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
opts := self.TransactOpts
|
||||
opts := ens.TransactOpts
|
||||
opts.GasLimit = 200000
|
||||
return resolver.Contract.SetContent(&opts, node, hash)
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ const (
|
||||
triesInMemory = 128
|
||||
|
||||
// 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
|
||||
@ -972,20 +972,26 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||
triedb.Cap(limit - ethdb.IdealBatchSize)
|
||||
}
|
||||
// Find the next state trie we need to commit
|
||||
header := bc.GetHeaderByNumber(current - triesInMemory)
|
||||
chosen := header.Number.Uint64()
|
||||
chosen := current - triesInMemory
|
||||
|
||||
// If we exceeded out time allowance, flush an entire trie to disk
|
||||
if bc.gcproc > bc.cacheConfig.TrieTimeLimit {
|
||||
// If we're exceeding limits but haven't reached a large enough memory gap,
|
||||
// warn the user that the system is becoming unstable.
|
||||
if chosen < lastWrite+triesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
|
||||
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/triesInMemory)
|
||||
// If the header is missing (canonical chain behind), we're reorging a low
|
||||
// diff sidechain. Suspend committing until this operation is completed.
|
||||
header := bc.GetHeaderByNumber(chosen)
|
||||
if header == nil {
|
||||
log.Warn("Reorg in progress, trie commit postponed", "number", chosen)
|
||||
} else {
|
||||
// If we're exceeding limits but haven't reached a large enough memory gap,
|
||||
// warn the user that the system is becoming unstable.
|
||||
if chosen < lastWrite+triesInMemory && bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
|
||||
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/triesInMemory)
|
||||
}
|
||||
// Flush an entire trie and restart the counters
|
||||
triedb.Commit(header.Root, true)
|
||||
lastWrite = chosen
|
||||
bc.gcproc = 0
|
||||
}
|
||||
// Flush an entire trie and restart the counters
|
||||
triedb.Commit(header.Root, true)
|
||||
lastWrite = chosen
|
||||
bc.gcproc = 0
|
||||
}
|
||||
// Garbage collect anything below our required write retention
|
||||
for !bc.triegc.Empty() {
|
||||
@ -1136,7 +1142,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||
switch {
|
||||
// First block is pruned, insert as sidechain and reorg only if TD grows enough
|
||||
case err == consensus.ErrPrunedAncestor:
|
||||
return bc.insertSidechain(it)
|
||||
return bc.insertSidechain(block, 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())):
|
||||
@ -1278,7 +1284,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
|
||||
//
|
||||
// The method writes all (header-and-body-valid) blocks to disk, then tries to
|
||||
// switch over to the new chain if the TD exceeded the current chain.
|
||||
func (bc *BlockChain) insertSidechain(it *insertIterator) (int, []interface{}, []*types.Log, error) {
|
||||
func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (int, []interface{}, []*types.Log, error) {
|
||||
var (
|
||||
externTd *big.Int
|
||||
current = bc.CurrentBlock().NumberU64()
|
||||
@ -1287,7 +1293,7 @@ func (bc *BlockChain) insertSidechain(it *insertIterator) (int, []interface{}, [
|
||||
// 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
|
||||
err := 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 >= number {
|
||||
@ -1317,7 +1323,7 @@ func (bc *BlockChain) insertSidechain(it *insertIterator) (int, []interface{}, [
|
||||
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(),
|
||||
log.Debug("Injected 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())
|
||||
|
@ -111,14 +111,6 @@ func (it *insertIterator) next() (*types.Block, error) {
|
||||
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 {
|
||||
|
@ -1483,3 +1483,58 @@ func BenchmarkBlockChain_1x1000Executions(b *testing.B) {
|
||||
|
||||
benchmarkLargeNumberOfValueToNonexisting(b, numTxs, numBlocks, recipientFn, dataFn)
|
||||
}
|
||||
|
||||
// Tests that importing a very large side fork, which is larger than the canon chain,
|
||||
// but where the difficulty per block is kept low: this means that it will not
|
||||
// overtake the 'canon' chain until after it's passed canon by about 200 blocks.
|
||||
//
|
||||
// Details at:
|
||||
// - https://github.com/ethereum/go-ethereum/issues/18977
|
||||
// - https://github.com/ethereum/go-ethereum/pull/18988
|
||||
func TestLowDiffLongChain(t *testing.T) {
|
||||
// Generate a canonical chain to act as the main dataset
|
||||
engine := ethash.NewFaker()
|
||||
db := ethdb.NewMemDatabase()
|
||||
genesis := new(Genesis).MustCommit(db)
|
||||
|
||||
// We must use a pretty long chain to ensure that the fork doesn't overtake us
|
||||
// until after at least 128 blocks post tip
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 6*triesInMemory, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{1})
|
||||
b.OffsetTime(-9)
|
||||
})
|
||||
|
||||
// Import the canonical chain
|
||||
diskdb := ethdb.NewMemDatabase()
|
||||
new(Genesis).MustCommit(diskdb)
|
||||
|
||||
chain, err := NewBlockChain(diskdb, nil, params.TestChainConfig, engine, vm.Config{}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create tester chain: %v", err)
|
||||
}
|
||||
if n, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
|
||||
}
|
||||
// Generate fork chain, starting from an early block
|
||||
parent := blocks[10]
|
||||
fork, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 8*triesInMemory, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{2})
|
||||
})
|
||||
|
||||
// And now import the fork
|
||||
if i, err := chain.InsertChain(fork); err != nil {
|
||||
t.Fatalf("block %d: failed to insert into chain: %v", i, err)
|
||||
}
|
||||
head := chain.CurrentBlock()
|
||||
if got := fork[len(fork)-1].Hash(); got != head.Hash() {
|
||||
t.Fatalf("head wrong, expected %x got %x", head.Hash(), got)
|
||||
}
|
||||
// Sanity check that all the canonical numbers are present
|
||||
header := chain.CurrentHeader()
|
||||
for number := head.NumberU64(); number > 0; number-- {
|
||||
if hash := chain.GetHeaderByNumber(number).Hash(); hash != header.Hash() {
|
||||
t.Fatalf("header %d: canonical hash mismatch: have %x, want %x", number, hash, header.Hash())
|
||||
}
|
||||
header = chain.GetHeader(header.ParentHash, number-1)
|
||||
}
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ func (b *BlockGen) PrevBlock(index int) *types.Block {
|
||||
// associated difficulty. It's useful to test scenarios where forking is not
|
||||
// tied to chain length directly.
|
||||
func (b *BlockGen) OffsetTime(seconds int64) {
|
||||
b.header.Time.Add(b.header.Time, new(big.Int).SetInt64(seconds))
|
||||
b.header.Time.Add(b.header.Time, big.NewInt(seconds))
|
||||
if b.header.Time.Cmp(b.parent.Header().Time) <= 0 {
|
||||
panic("block time out of range")
|
||||
}
|
||||
|
@ -157,7 +157,6 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, constant
|
||||
if genesis != nil && genesis.Config == nil {
|
||||
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
|
||||
}
|
||||
|
||||
// Just commit the new block if there is no stored genesis block.
|
||||
stored := rawdb.ReadCanonicalHash(db, 0)
|
||||
if (stored == common.Hash{}) {
|
||||
@ -183,6 +182,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, constant
|
||||
newcfg := genesis.configOrDefault(stored)
|
||||
if constantinopleOverride != nil {
|
||||
newcfg.ConstantinopleBlock = constantinopleOverride
|
||||
newcfg.PetersburgBlock = constantinopleOverride
|
||||
}
|
||||
storedcfg := rawdb.ReadChainConfig(db, stored)
|
||||
if storedcfg == nil {
|
||||
@ -339,6 +339,18 @@ func DefaultRinkebyGenesisBlock() *Genesis {
|
||||
}
|
||||
}
|
||||
|
||||
// DefaultGoerliGenesisBlock returns the Görli network genesis block.
|
||||
func DefaultGoerliGenesisBlock() *Genesis {
|
||||
return &Genesis{
|
||||
Config: params.GoerliChainConfig,
|
||||
Timestamp: 1548854791,
|
||||
ExtraData: hexutil.MustDecode("0x22466c6578692069732061207468696e6722202d204166726900000000000000e0a2bd4258d2768837baa26a28fe71dc079f84c70000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
|
||||
GasLimit: 10485760,
|
||||
Difficulty: big.NewInt(1),
|
||||
Alloc: decodePrealloc(goerliAllocData),
|
||||
}
|
||||
}
|
||||
|
||||
// DeveloperGenesisBlock returns the 'geth --dev' genesis block. Note, this must
|
||||
// be seeded with the
|
||||
func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
|
||||
|
File diff suppressed because one or more lines are too long
@ -23,9 +23,9 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// 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 {
|
||||
t.Fatalf("Stored header RLP not found")
|
||||
} else {
|
||||
hasher := sha3.NewKeccak256()
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher.Write(entry)
|
||||
|
||||
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
|
||||
body := &types.Body{Uncles: []*types.Header{{Extra: []byte("test header")}}}
|
||||
|
||||
hasher := sha3.NewKeccak256()
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
rlp.Encode(hasher, body)
|
||||
hash := common.BytesToHash(hasher.Sum(nil))
|
||||
|
||||
@ -85,7 +85,7 @@ func TestBodyStorage(t *testing.T) {
|
||||
if entry := ReadBodyRLP(db, hash, 0); entry == nil {
|
||||
t.Fatalf("Stored body RLP not found")
|
||||
} else {
|
||||
hasher := sha3.NewKeccak256()
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
hasher.Write(entry)
|
||||
|
||||
if calc := common.BytesToHash(hasher.Sum(nil)); calc != hash {
|
||||
|
@ -26,19 +26,27 @@ import (
|
||||
)
|
||||
|
||||
// ReadDatabaseVersion retrieves the version number of the database.
|
||||
func ReadDatabaseVersion(db DatabaseReader) int {
|
||||
var version int
|
||||
func ReadDatabaseVersion(db DatabaseReader) *uint64 {
|
||||
var version uint64
|
||||
|
||||
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
|
||||
func WriteDatabaseVersion(db DatabaseWriter, version int) {
|
||||
enc, _ := rlp.EncodeToBytes(version)
|
||||
if err := db.Put(databaseVerisionKey, enc); err != nil {
|
||||
func WriteDatabaseVersion(db DatabaseWriter, version uint64) {
|
||||
enc, err := rlp.EncodeToBytes(version)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -468,9 +468,9 @@ func (self *StateDB) createObject(addr common.Address) (newobj, prev *stateObjec
|
||||
//
|
||||
// Carrying over the balance ensures that Ether doesn't disappear.
|
||||
func (self *StateDB) CreateAccount(addr common.Address) {
|
||||
new, prev := self.createObject(addr)
|
||||
newObj, prev := self.createObject(addr)
|
||||
if prev != nil {
|
||||
new.setBalance(prev.data.Balance)
|
||||
newObj.setBalance(prev.data.Balance)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ import (
|
||||
"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())
|
||||
|
||||
// txSenderCacherRequest is a request for recovering transaction senders with a
|
||||
|
@ -172,6 +172,26 @@ func (config *TxPoolConfig) sanitize() TxPoolConfig {
|
||||
log.Warn("Sanitizing invalid txpool price bump", "provided", conf.PriceBump, "updated", 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
|
||||
}
|
||||
|
||||
|
@ -1095,7 +1095,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
|
||||
blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
|
||||
|
||||
config := testTxPoolConfig
|
||||
config.GlobalSlots = 0
|
||||
config.GlobalSlots = 1
|
||||
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
@ -28,8 +28,8 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -109,7 +109,7 @@ func (h *Header) Size() common.StorageSize {
|
||||
}
|
||||
|
||||
func rlpHash(x interface{}) (h common.Hash) {
|
||||
hw := sha3.NewKeccak256()
|
||||
hw := sha3.NewLegacyKeccak256()
|
||||
rlp.Encode(hw, x)
|
||||
hw.Sum(h[:0])
|
||||
return h
|
||||
|
@ -234,7 +234,7 @@ func (tx *Transaction) AsMessage(s Signer) (Message, error) {
|
||||
}
|
||||
|
||||
// WithSignature returns a new transaction with the given signature.
|
||||
// This signature needs to be formatted as described in the yellow paper (v+27).
|
||||
// This signature needs to be in the [R || S || V] format where V is 0 or 1.
|
||||
func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, error) {
|
||||
r, s, v, err := signer.SignatureValues(tx, sig)
|
||||
if err != nil {
|
||||
|
@ -121,7 +121,9 @@ func gasSStore(gt params.GasTable, evm *EVM, contract *Contract, stack *Stack, m
|
||||
current = evm.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
)
|
||||
// The legacy gas metering only takes into consideration the current state
|
||||
if !evm.chainRules.IsConstantinople {
|
||||
// Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
|
||||
// OR Constantinople is not active
|
||||
if evm.chainRules.IsPetersburg || !evm.chainRules.IsConstantinople {
|
||||
// This checks for 3 scenario's and calculates gas accordingly:
|
||||
//
|
||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||
|
@ -24,8 +24,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -387,7 +387,7 @@ func opSha3(pc *uint64, interpreter *EVMInterpreter, contract *Contract, memory
|
||||
data := memory.Get(offset.Int64(), size.Int64())
|
||||
|
||||
if interpreter.hasher == nil {
|
||||
interpreter.hasher = sha3.NewKeccak256().(keccakState)
|
||||
interpreter.hasher = sha3.NewLegacyKeccak256().(keccakState)
|
||||
} else {
|
||||
interpreter.hasher.Reset()
|
||||
}
|
||||
|
@ -34,7 +34,11 @@ type JSONLogger struct {
|
||||
// NewJSONLogger creates a new EVM tracer that prints execution steps as JSON objects
|
||||
// into the provided stream.
|
||||
func NewJSONLogger(cfg *LogConfig, writer io.Writer) *JSONLogger {
|
||||
return &JSONLogger{json.NewEncoder(writer), cfg}
|
||||
l := &JSONLogger{json.NewEncoder(writer), cfg}
|
||||
if l.cfg == nil {
|
||||
l.cfg = &LogConfig{}
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *JSONLogger) CaptureStart(from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) error {
|
||||
|
@ -30,8 +30,8 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -43,7 +43,7 @@ var errInvalidPubkey = errors.New("invalid secp256k1 public key")
|
||||
|
||||
// Keccak256 calculates and returns the Keccak256 hash of the input data.
|
||||
func Keccak256(data ...[]byte) []byte {
|
||||
d := sha3.NewKeccak256()
|
||||
d := sha3.NewLegacyKeccak256()
|
||||
for _, b := range data {
|
||||
d.Write(b)
|
||||
}
|
||||
@ -53,7 +53,7 @@ func Keccak256(data ...[]byte) []byte {
|
||||
// Keccak256Hash calculates and returns the Keccak256 hash of the input data,
|
||||
// converting it to an internal Hash data structure.
|
||||
func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
||||
d := sha3.NewKeccak256()
|
||||
d := sha3.NewLegacyKeccak256()
|
||||
for _, b := range data {
|
||||
d.Write(b)
|
||||
}
|
||||
@ -63,7 +63,7 @@ func Keccak256Hash(data ...[]byte) (h common.Hash) {
|
||||
|
||||
// Keccak512 calculates and returns the Keccak512 hash of the input data.
|
||||
func Keccak512(data ...[]byte) []byte {
|
||||
d := sha3.NewKeccak512()
|
||||
d := sha3.NewLegacyKeccak512()
|
||||
for _, b := range data {
|
||||
d.Write(b)
|
||||
}
|
||||
|
@ -310,7 +310,7 @@ var theCurve = new(BitCurve)
|
||||
func init() {
|
||||
// See SEC 2 section 2.7.1
|
||||
// curve parameters taken from:
|
||||
// http://www.secg.org/collateral/sec2_final.pdf
|
||||
// http://www.secg.org/sec2-v2.pdf
|
||||
theCurve.P, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 0)
|
||||
theCurve.N, _ = new(big.Int).SetString("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 0)
|
||||
theCurve.B, _ = new(big.Int).SetString("0x0000000000000000000000000000000000000000000000000000000000000007", 0)
|
||||
|
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@ -1,22 +0,0 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
@ -1,297 +0,0 @@
|
||||
// Copyright 2014 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package sha3
|
||||
|
||||
// Tests include all the ShortMsgKATs provided by the Keccak team at
|
||||
// https://github.com/gvanas/KeccakCodePackage
|
||||
//
|
||||
// They only include the zero-bit case of the bitwise testvectors
|
||||
// published by NIST in the draft of FIPS-202.
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/flate"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"hash"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
testString = "brekeccakkeccak koax koax"
|
||||
katFilename = "testdata/keccakKats.json.deflate"
|
||||
)
|
||||
|
||||
// Internal-use instances of SHAKE used to test against KATs.
|
||||
func newHashShake128() hash.Hash {
|
||||
return &state{rate: 168, dsbyte: 0x1f, outputLen: 512}
|
||||
}
|
||||
func newHashShake256() hash.Hash {
|
||||
return &state{rate: 136, dsbyte: 0x1f, outputLen: 512}
|
||||
}
|
||||
|
||||
// testDigests contains functions returning hash.Hash instances
|
||||
// with output-length equal to the KAT length for both SHA-3 and
|
||||
// SHAKE instances.
|
||||
var testDigests = map[string]func() hash.Hash{
|
||||
"SHA3-224": New224,
|
||||
"SHA3-256": New256,
|
||||
"SHA3-384": New384,
|
||||
"SHA3-512": New512,
|
||||
"SHAKE128": newHashShake128,
|
||||
"SHAKE256": newHashShake256,
|
||||
}
|
||||
|
||||
// testShakes contains functions that return ShakeHash instances for
|
||||
// testing the ShakeHash-specific interface.
|
||||
var testShakes = map[string]func() ShakeHash{
|
||||
"SHAKE128": NewShake128,
|
||||
"SHAKE256": NewShake256,
|
||||
}
|
||||
|
||||
// structs used to marshal JSON test-cases.
|
||||
type KeccakKats struct {
|
||||
Kats map[string][]struct {
|
||||
Digest string `json:"digest"`
|
||||
Length int64 `json:"length"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
}
|
||||
|
||||
func testUnalignedAndGeneric(t *testing.T, testf func(impl string)) {
|
||||
xorInOrig, copyOutOrig := xorIn, copyOut
|
||||
xorIn, copyOut = xorInGeneric, copyOutGeneric
|
||||
testf("generic")
|
||||
if xorImplementationUnaligned != "generic" {
|
||||
xorIn, copyOut = xorInUnaligned, copyOutUnaligned
|
||||
testf("unaligned")
|
||||
}
|
||||
xorIn, copyOut = xorInOrig, copyOutOrig
|
||||
}
|
||||
|
||||
// TestKeccakKats tests the SHA-3 and Shake implementations against all the
|
||||
// ShortMsgKATs from https://github.com/gvanas/KeccakCodePackage
|
||||
// (The testvectors are stored in keccakKats.json.deflate due to their length.)
|
||||
func TestKeccakKats(t *testing.T) {
|
||||
testUnalignedAndGeneric(t, func(impl string) {
|
||||
// Read the KATs.
|
||||
deflated, err := os.Open(katFilename)
|
||||
if err != nil {
|
||||
t.Errorf("error opening %s: %s", katFilename, err)
|
||||
}
|
||||
file := flate.NewReader(deflated)
|
||||
dec := json.NewDecoder(file)
|
||||
var katSet KeccakKats
|
||||
err = dec.Decode(&katSet)
|
||||
if err != nil {
|
||||
t.Errorf("error decoding KATs: %s", err)
|
||||
}
|
||||
|
||||
// Do the KATs.
|
||||
for functionName, kats := range katSet.Kats {
|
||||
d := testDigests[functionName]()
|
||||
for _, kat := range kats {
|
||||
d.Reset()
|
||||
in, err := hex.DecodeString(kat.Message)
|
||||
if err != nil {
|
||||
t.Errorf("error decoding KAT: %s", err)
|
||||
}
|
||||
d.Write(in[:kat.Length/8])
|
||||
got := strings.ToUpper(hex.EncodeToString(d.Sum(nil)))
|
||||
if got != kat.Digest {
|
||||
t.Errorf("function=%s, implementation=%s, length=%d\nmessage:\n %s\ngot:\n %s\nwanted:\n %s",
|
||||
functionName, impl, kat.Length, kat.Message, got, kat.Digest)
|
||||
t.Logf("wanted %+v", kat)
|
||||
t.FailNow()
|
||||
}
|
||||
continue
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestUnalignedWrite tests that writing data in an arbitrary pattern with
|
||||
// small input buffers.
|
||||
func TestUnalignedWrite(t *testing.T) {
|
||||
testUnalignedAndGeneric(t, func(impl string) {
|
||||
buf := sequentialBytes(0x10000)
|
||||
for alg, df := range testDigests {
|
||||
d := df()
|
||||
d.Reset()
|
||||
d.Write(buf)
|
||||
want := d.Sum(nil)
|
||||
d.Reset()
|
||||
for i := 0; i < len(buf); {
|
||||
// Cycle through offsets which make a 137 byte sequence.
|
||||
// Because 137 is prime this sequence should exercise all corner cases.
|
||||
offsets := [17]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 1}
|
||||
for _, j := range offsets {
|
||||
if v := len(buf) - i; v < j {
|
||||
j = v
|
||||
}
|
||||
d.Write(buf[i : i+j])
|
||||
i += j
|
||||
}
|
||||
}
|
||||
got := d.Sum(nil)
|
||||
if !bytes.Equal(got, want) {
|
||||
t.Errorf("Unaligned writes, implementation=%s, alg=%s\ngot %q, want %q", impl, alg, got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestAppend checks that appending works when reallocation is necessary.
|
||||
func TestAppend(t *testing.T) {
|
||||
testUnalignedAndGeneric(t, func(impl string) {
|
||||
d := New224()
|
||||
|
||||
for capacity := 2; capacity <= 66; capacity += 64 {
|
||||
// The first time around the loop, Sum will have to reallocate.
|
||||
// The second time, it will not.
|
||||
buf := make([]byte, 2, capacity)
|
||||
d.Reset()
|
||||
d.Write([]byte{0xcc})
|
||||
buf = d.Sum(buf)
|
||||
expected := "0000DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
|
||||
if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
|
||||
t.Errorf("got %s, want %s", got, expected)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestAppendNoRealloc tests that appending works when no reallocation is necessary.
|
||||
func TestAppendNoRealloc(t *testing.T) {
|
||||
testUnalignedAndGeneric(t, func(impl string) {
|
||||
buf := make([]byte, 1, 200)
|
||||
d := New224()
|
||||
d.Write([]byte{0xcc})
|
||||
buf = d.Sum(buf)
|
||||
expected := "00DF70ADC49B2E76EEE3A6931B93FA41841C3AF2CDF5B32A18B5478C39"
|
||||
if got := strings.ToUpper(hex.EncodeToString(buf)); got != expected {
|
||||
t.Errorf("%s: got %s, want %s", impl, got, expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestSqueezing checks that squeezing the full output a single time produces
|
||||
// the same output as repeatedly squeezing the instance.
|
||||
func TestSqueezing(t *testing.T) {
|
||||
testUnalignedAndGeneric(t, func(impl string) {
|
||||
for functionName, newShakeHash := range testShakes {
|
||||
d0 := newShakeHash()
|
||||
d0.Write([]byte(testString))
|
||||
ref := make([]byte, 32)
|
||||
d0.Read(ref)
|
||||
|
||||
d1 := newShakeHash()
|
||||
d1.Write([]byte(testString))
|
||||
var multiple []byte
|
||||
for range ref {
|
||||
one := make([]byte, 1)
|
||||
d1.Read(one)
|
||||
multiple = append(multiple, one...)
|
||||
}
|
||||
if !bytes.Equal(ref, multiple) {
|
||||
t.Errorf("%s (%s): squeezing %d bytes one at a time failed", functionName, impl, len(ref))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// sequentialBytes produces a buffer of size consecutive bytes 0x00, 0x01, ..., used for testing.
|
||||
func sequentialBytes(size int) []byte {
|
||||
result := make([]byte, size)
|
||||
for i := range result {
|
||||
result[i] = byte(i)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// BenchmarkPermutationFunction measures the speed of the permutation function
|
||||
// with no input data.
|
||||
func BenchmarkPermutationFunction(b *testing.B) {
|
||||
b.SetBytes(int64(200))
|
||||
var lanes [25]uint64
|
||||
for i := 0; i < b.N; i++ {
|
||||
keccakF1600(&lanes)
|
||||
}
|
||||
}
|
||||
|
||||
// benchmarkHash tests the speed to hash num buffers of buflen each.
|
||||
func benchmarkHash(b *testing.B, h hash.Hash, size, num int) {
|
||||
b.StopTimer()
|
||||
h.Reset()
|
||||
data := sequentialBytes(size)
|
||||
b.SetBytes(int64(size * num))
|
||||
b.StartTimer()
|
||||
|
||||
var state []byte
|
||||
for i := 0; i < b.N; i++ {
|
||||
for j := 0; j < num; j++ {
|
||||
h.Write(data)
|
||||
}
|
||||
state = h.Sum(state[:0])
|
||||
}
|
||||
b.StopTimer()
|
||||
h.Reset()
|
||||
}
|
||||
|
||||
// benchmarkShake is specialized to the Shake instances, which don't
|
||||
// require a copy on reading output.
|
||||
func benchmarkShake(b *testing.B, h ShakeHash, size, num int) {
|
||||
b.StopTimer()
|
||||
h.Reset()
|
||||
data := sequentialBytes(size)
|
||||
d := make([]byte, 32)
|
||||
|
||||
b.SetBytes(int64(size * num))
|
||||
b.StartTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
h.Reset()
|
||||
for j := 0; j < num; j++ {
|
||||
h.Write(data)
|
||||
}
|
||||
h.Read(d)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkSha3_512_MTU(b *testing.B) { benchmarkHash(b, New512(), 1350, 1) }
|
||||
func BenchmarkSha3_384_MTU(b *testing.B) { benchmarkHash(b, New384(), 1350, 1) }
|
||||
func BenchmarkSha3_256_MTU(b *testing.B) { benchmarkHash(b, New256(), 1350, 1) }
|
||||
func BenchmarkSha3_224_MTU(b *testing.B) { benchmarkHash(b, New224(), 1350, 1) }
|
||||
|
||||
func BenchmarkShake128_MTU(b *testing.B) { benchmarkShake(b, NewShake128(), 1350, 1) }
|
||||
func BenchmarkShake256_MTU(b *testing.B) { benchmarkShake(b, NewShake256(), 1350, 1) }
|
||||
func BenchmarkShake256_16x(b *testing.B) { benchmarkShake(b, NewShake256(), 16, 1024) }
|
||||
func BenchmarkShake256_1MiB(b *testing.B) { benchmarkShake(b, NewShake256(), 1024, 1024) }
|
||||
|
||||
func BenchmarkSha3_512_1MiB(b *testing.B) { benchmarkHash(b, New512(), 1024, 1024) }
|
||||
|
||||
func Example_sum() {
|
||||
buf := []byte("some data to hash")
|
||||
// A hash needs to be 64 bytes long to have 256-bit collision resistance.
|
||||
h := make([]byte, 64)
|
||||
// Compute a 64-byte hash of buf and put it in h.
|
||||
ShakeSum256(h, buf)
|
||||
}
|
||||
|
||||
func Example_mac() {
|
||||
k := []byte("this is a secret key; you should generate a strong random key that's at least 32 bytes long")
|
||||
buf := []byte("and this is some data to authenticate")
|
||||
// A MAC with 32 bytes of output has 256-bit security strength -- if you use at least a 32-byte-long key.
|
||||
h := make([]byte, 32)
|
||||
d := NewShake256()
|
||||
// Write the key into the hash.
|
||||
d.Write(k)
|
||||
// Now write the data.
|
||||
d.Write(buf)
|
||||
// Read 32 bytes of output from the hash into h.
|
||||
d.Read(h)
|
||||
}
|
BIN
crypto/sha3/testdata/keccakKats.json.deflate
vendored
BIN
crypto/sha3/testdata/keccakKats.json.deflate
vendored
Binary file not shown.
@ -214,7 +214,8 @@ func (api *PrivateDebugAPI) traceChain(ctx context.Context, start, end *types.Bl
|
||||
log.Warn("Tracing failed", "hash", tx.Hash(), "block", task.block.NumberU64(), "err", err)
|
||||
break
|
||||
}
|
||||
task.statedb.Finalise(true)
|
||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||
task.statedb.Finalise(api.eth.blockchain.Config().IsEIP158(task.block.Number()))
|
||||
task.results[i] = &txTraceResult{Result: res}
|
||||
}
|
||||
// Stream the result back to the user or abort on teardown
|
||||
@ -506,7 +507,8 @@ func (api *PrivateDebugAPI) traceBlock(ctx context.Context, block *types.Block,
|
||||
break
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
statedb.Finalise(true)
|
||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
}
|
||||
close(jobs)
|
||||
pend.Wait()
|
||||
@ -608,7 +610,8 @@ func (api *PrivateDebugAPI) standardTraceBlockToFile(ctx context.Context, block
|
||||
return dumps, err
|
||||
}
|
||||
// Finalize the state so any modifications are written to the trie
|
||||
statedb.Finalise(true)
|
||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
|
||||
// If we've traced the transaction we were looking for, abort
|
||||
if tx.Hash() == txHash {
|
||||
@ -799,7 +802,8 @@ func (api *PrivateDebugAPI) computeTxEnv(blockHash common.Hash, txIndex int, ree
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("transaction %#x failed: %v", tx.Hash(), err)
|
||||
}
|
||||
// Ensure any modifications are committed to the state
|
||||
statedb.Finalise(true)
|
||||
// Only delete empty objects if EIP158/161 (a.k.a Spurious Dragon) is in effect
|
||||
statedb.Finalise(vmenv.ChainConfig().IsEIP158(block.Number()))
|
||||
}
|
||||
return nil, vm.Context{}, nil, fmt.Errorf("transaction index %d out of range for block %#x", txIndex, blockHash)
|
||||
}
|
||||
|
@ -143,8 +143,10 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
|
||||
if !config.SkipBcVersionCheck {
|
||||
bcVersion := rawdb.ReadDatabaseVersion(chainDb)
|
||||
if bcVersion != core.BlockChainVersion && bcVersion != 0 {
|
||||
return nil, fmt.Errorf("Blockchain DB version mismatch (%d / %d).\n", bcVersion, core.BlockChainVersion)
|
||||
if bcVersion != nil && *bcVersion > core.BlockChainVersion {
|
||||
return nil, fmt.Errorf("database version is v%d, Geth %s only supports v%d", *bcVersion, params.VersionWithMeta, core.BlockChainVersion)
|
||||
} else if bcVersion != nil && *bcVersion < core.BlockChainVersion {
|
||||
log.Warn("Upgrade blockchain database version", "from", *bcVersion, "to", core.BlockChainVersion)
|
||||
}
|
||||
rawdb.WriteDatabaseVersion(chainDb, core.BlockChainVersion)
|
||||
}
|
||||
|
@ -1488,7 +1488,15 @@ func (d *Downloader) importBlockResults(results []*fetchResult) error {
|
||||
blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
|
||||
}
|
||||
if index, err := d.blockchain.InsertChain(blocks); err != nil {
|
||||
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
|
||||
if index < len(results) {
|
||||
log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
|
||||
} else {
|
||||
// The InsertChain method in blockchain.go will sometimes return an out-of-bounds index,
|
||||
// when it needs to preprocess blocks to import a sidechain.
|
||||
// The importer will put together a new list of blocks to import, which is a superset
|
||||
// of the blocks delivered from the downloader, and the indexing will be off.
|
||||
log.Debug("Downloaded item processing failed on sidechain import", "index", index, "err", err)
|
||||
}
|
||||
return errInvalidChain
|
||||
}
|
||||
return nil
|
||||
|
@ -25,10 +25,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// stateReq represents a batch of state fetch requests grouped together into
|
||||
@ -152,7 +152,7 @@ func (d *Downloader) runStateSync(s *stateSync) *stateSync {
|
||||
finished = append(finished, req)
|
||||
delete(active, pack.PeerId())
|
||||
|
||||
// Handle dropped peer connections:
|
||||
// Handle dropped peer connections:
|
||||
case p := <-peerDrop:
|
||||
// Skip if no request is currently pending
|
||||
req := active[p.id]
|
||||
@ -240,7 +240,7 @@ func newStateSync(d *Downloader, root common.Hash) *stateSync {
|
||||
return &stateSync{
|
||||
d: d,
|
||||
sched: state.NewStateSync(root, d.stateDB),
|
||||
keccak: sha3.NewKeccak256(),
|
||||
keccak: sha3.NewLegacyKeccak256(),
|
||||
tasks: make(map[common.Hash]*stateTask),
|
||||
deliver: make(chan *stateReq),
|
||||
cancel: make(chan struct{}),
|
||||
@ -398,9 +398,8 @@ func (s *stateSync) fillTasks(n int, req *stateReq) {
|
||||
|
||||
// process iterates over a batch of delivered state data, injecting each item
|
||||
// into a running state sync, re-queuing any items that were requested but not
|
||||
// delivered.
|
||||
// Returns whether the peer actually managed to deliver anything of value,
|
||||
// and any error that occurred
|
||||
// delivered. Returns whether the peer actually managed to deliver anything of
|
||||
// value, and any error that occurred.
|
||||
func (s *stateSync) process(req *stateReq) (int, error) {
|
||||
// Collect processing stats and update progress if valid data was received
|
||||
duplicate, unexpected, successful := 0, 0, 0
|
||||
@ -412,14 +411,12 @@ func (s *stateSync) process(req *stateReq) (int, error) {
|
||||
}(time.Now())
|
||||
|
||||
// Iterate over all the delivered data and inject one-by-one into the trie
|
||||
progress := false
|
||||
for _, blob := range req.response {
|
||||
prog, hash, err := s.processNodeData(blob)
|
||||
_, hash, err := s.processNodeData(blob)
|
||||
switch err {
|
||||
case nil:
|
||||
s.numUncommitted++
|
||||
s.bytesUncommitted += len(blob)
|
||||
progress = progress || prog
|
||||
successful++
|
||||
case trie.ErrNotRequested:
|
||||
unexpected++
|
||||
|
File diff suppressed because one or more lines are too long
@ -38,7 +38,7 @@
|
||||
var op = log.op.toString();
|
||||
}
|
||||
// If a new contract is being created, add to the call stack
|
||||
if (syscall && op == 'CREATE') {
|
||||
if (syscall && (op == 'CREATE' || op == "CREATE2")) {
|
||||
var inOff = log.stack.peek(1).valueOf();
|
||||
var inEnd = inOff + log.stack.peek(2).valueOf();
|
||||
|
||||
@ -116,7 +116,7 @@
|
||||
// Pop off the last call and get the execution results
|
||||
var call = this.callstack.pop();
|
||||
|
||||
if (call.type == 'CREATE') {
|
||||
if (call.type == 'CREATE' || call.type == "CREATE2") {
|
||||
// If the call was a CREATE, retrieve the contract address and output code
|
||||
call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost - log.getGas()).toString(16);
|
||||
delete call.gasIn; delete call.gasCost;
|
||||
|
@ -86,6 +86,14 @@
|
||||
var from = log.contract.getAddress();
|
||||
this.lookupAccount(toContract(from, db.getNonce(from)), db);
|
||||
break;
|
||||
case "CREATE2":
|
||||
var from = log.contract.getAddress();
|
||||
// stack: salt, size, offset, endowment
|
||||
var offset = log.stack.peek(1).valueOf()
|
||||
var size = log.stack.peek(2).valueOf()
|
||||
var end = offset + size
|
||||
this.lookupAccount(toContract2(from, log.stack.peek(3).toString(16), log.memory.slice(offset, end)), db);
|
||||
break;
|
||||
case "CALL": case "CALLCODE": case "DELEGATECALL": case "STATICCALL":
|
||||
this.lookupAccount(toAddress(log.stack.peek(1).toString(16)), db);
|
||||
break;
|
||||
|
@ -14,7 +14,7 @@
|
||||
// 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/>.
|
||||
|
||||
//go:generate go-bindata -nometadata -o assets.go -pkg tracers -ignore ((tracers)|(assets)).go ./...
|
||||
//go:generate go-bindata -nometadata -o assets.go -pkg tracers -ignore tracers.go -ignore assets.go ./...
|
||||
//go:generate gofmt -s -w assets.go
|
||||
|
||||
// Package tracers contains the actual JavaScript tracer assets.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user