Compare commits
208 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
4e13a09c50 | ||
|
009d2fe2d6 | ||
|
e872ba7a9e | ||
|
9d9c6b5847 | ||
|
8ca6454807 | ||
|
0e63a70505 | ||
|
f1b00cffc8 | ||
|
442320a8ae | ||
|
af401d03a3 | ||
|
80a2a35bc3 | ||
|
fca5f9fd6f | ||
|
38c30f8dd8 | ||
|
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 | ||
|
24d727b6d6 | ||
|
83a9a73b89 | ||
|
5584574217 | ||
|
38c3d88cea | ||
|
69a8d9841a | ||
|
bb724080ca | ||
|
b2aac658b0 | ||
|
9fe5d20011 | ||
|
dd98d1da94 | ||
|
362e2ba792 | ||
|
31b3334922 | ||
|
48b70ecff1 | ||
|
c1e3fe6b14 | ||
|
2fdff33803 | ||
|
da6e6e7971 | ||
|
af8daf91a6 | ||
|
fd66af5ee5 | ||
|
0983d02aa9 | ||
|
42a914a84f | ||
|
09d588e0da | ||
|
6a1a4375c6 | ||
|
dfa16a3e4e | ||
|
c1d462ee5d | ||
|
f32790fb05 | ||
|
d2328b604a | ||
|
661809714e | ||
|
de39513ced | ||
|
3ac633ba84 | ||
|
b98d2e9a1c | ||
|
92639b676a | ||
|
f74077b4c2 | ||
|
d4415f5e40 | ||
|
7a5c1b28dd | ||
|
8698fbabf6 | ||
|
a3fd415c0f | ||
|
4825d9c3dd | ||
|
f850123ec7 | ||
|
efe5886877 | ||
|
085f89172f | ||
|
54abb97e3b | ||
|
ef8ced4151 | ||
|
7e7781ffaa | ||
|
cf62bd2e88 | ||
|
01371469e6 | ||
|
32d35c9c08 | ||
|
a4428c505e | ||
|
55a4ff806f | ||
|
8380a1303c | ||
|
7c657fc789 | ||
|
3d21d455dc | ||
|
3dba6a6d27 | ||
|
a7501d0c41 | ||
|
21dd59bd04 |
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
|
||||
|
23
.travis.yml
23
.travis.yml
@@ -4,7 +4,7 @@ sudo: false
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
sudo: required
|
||||
go: 1.10.x
|
||||
script:
|
||||
@@ -16,7 +16,7 @@ matrix:
|
||||
|
||||
# These are the latest Go versions.
|
||||
- os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
sudo: required
|
||||
go: 1.11.x
|
||||
script:
|
||||
@@ -43,7 +43,7 @@ matrix:
|
||||
|
||||
# This builder only tests code linters on latest version of Go
|
||||
- os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
go: 1.11.x
|
||||
env:
|
||||
- lint
|
||||
@@ -55,7 +55,7 @@ matrix:
|
||||
# This builder does the Ubuntu PPA upload
|
||||
- if: type = push
|
||||
os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
go: 1.11.x
|
||||
env:
|
||||
- ubuntu-ppa
|
||||
@@ -68,13 +68,16 @@ 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
|
||||
os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
sudo: required
|
||||
go: 1.11.x
|
||||
env:
|
||||
@@ -108,7 +111,7 @@ matrix:
|
||||
# This builder does the Linux Azure MIPS xgo uploads
|
||||
- if: type = push
|
||||
os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
services:
|
||||
- docker
|
||||
go: 1.11.x
|
||||
@@ -136,7 +139,7 @@ matrix:
|
||||
# This builder does the Android Maven and Azure uploads
|
||||
- if: type = push
|
||||
os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
@@ -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
|
||||
@@ -203,7 +206,7 @@ matrix:
|
||||
# This builder does the Azure archive purges to avoid accumulating junk
|
||||
- if: type = cron
|
||||
os: linux
|
||||
dist: trusty
|
||||
dist: xenial
|
||||
go: 1.11.x
|
||||
env:
|
||||
- azure-purge
|
||||
|
@@ -168,7 +168,7 @@ HTTP based JSON-RPC API options:
|
||||
* `--ipcpath` Filename for IPC socket/pipe within the datadir (explicit paths escape it)
|
||||
|
||||
You'll need to use your own programming environments' capabilities (libraries, tools, etc) to connect
|
||||
via HTTP, WS or IPC to a Geth node configured with the above flags and you'll need to speak [JSON-RPC](http://www.jsonrpc.org/specification)
|
||||
via HTTP, WS or IPC to a Geth node configured with the above flags and you'll need to speak [JSON-RPC](https://www.jsonrpc.org/specification)
|
||||
on all transports. You can reuse the same connection for multiple requests!
|
||||
|
||||
**Note: Please understand the security implications of opening up an HTTP/WS based transport before
|
||||
|
@@ -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,11 +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 {
|
||||
if abiArg.Type.T == ArrayTy {
|
||||
inputOffset += 32 * abiArg.Type.Size
|
||||
} else {
|
||||
inputOffset += 32
|
||||
}
|
||||
inputOffset += getTypeSize(abiArg.Type)
|
||||
}
|
||||
var ret []byte
|
||||
for i, a := range args {
|
||||
@@ -257,14 +300,13 @@ func (arguments Arguments) Pack(args ...interface{}) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check for a slice type (string, bytes, slice)
|
||||
if input.Type.requiresLengthPrefix() {
|
||||
// calculate the offset
|
||||
offset := inputOffset + len(variableInput)
|
||||
// check for dynamic types
|
||||
if isDynamicType(input.Type) {
|
||||
// set the offset
|
||||
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
||||
// Append the packed output to the variable input. The variable input
|
||||
// will be appended at the end of the input.
|
||||
ret = append(ret, packNum(reflect.ValueOf(inputOffset))...)
|
||||
// calculate next offset
|
||||
inputOffset += len(packed)
|
||||
// append to variable input
|
||||
variableInput = append(variableInput, packed...)
|
||||
} else {
|
||||
// append the packed value to the input
|
||||
@@ -277,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,314 +29,601 @@ 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
|
||||
"0000000000000000000000000000000000000000000000000000000000000080" + // offset 128 to i = 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
||||
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
||||
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
||||
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
|
||||
},
|
||||
{
|
||||
"string[2]",
|
||||
nil,
|
||||
[]string{"hello", "foobar"},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset to i = 0
|
||||
"0000000000000000000000000000000000000000000000000000000000000080" + // offset to i = 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000005" + // len(str[0]) = 5
|
||||
"68656c6c6f000000000000000000000000000000000000000000000000000000" + // str[0]
|
||||
"0000000000000000000000000000000000000000000000000000000000000006" + // len(str[1]) = 6
|
||||
"666f6f6261720000000000000000000000000000000000000000000000000000"), // str[1]
|
||||
},
|
||||
{
|
||||
"bytes32[][]",
|
||||
nil,
|
||||
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000002" + // len(array) = 2
|
||||
"0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
||||
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
||||
},
|
||||
|
||||
{
|
||||
"bytes32[][2]",
|
||||
nil,
|
||||
[][]common.Hash{{{1}, {2}}, {{3}, {4}, {5}}},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000040" + // offset 64 to i = 0
|
||||
"00000000000000000000000000000000000000000000000000000000000000a0" + // offset 160 to i = 1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // len(array[0]) = 2
|
||||
"0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||
"0000000000000000000000000000000000000000000000000000000000000003" + // len(array[1]) = 3
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
||||
},
|
||||
|
||||
{
|
||||
"bytes32[3][2]",
|
||||
nil,
|
||||
[][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}},
|
||||
common.Hex2Bytes("0100000000000000000000000000000000000000000000000000000000000000" + // array[0][0]
|
||||
"0200000000000000000000000000000000000000000000000000000000000000" + // array[0][1]
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[0][2]
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // array[1][0]
|
||||
"0400000000000000000000000000000000000000000000000000000000000000" + // array[1][1]
|
||||
"0500000000000000000000000000000000000000000000000000000000000000"), // array[1][2]
|
||||
},
|
||||
{
|
||||
// static tuple
|
||||
"tuple",
|
||||
[]ArgumentMarshaling{
|
||||
{Name: "a", Type: "int64"},
|
||||
{Name: "b", Type: "int256"},
|
||||
{Name: "c", Type: "int256"},
|
||||
{Name: "d", Type: "bool"},
|
||||
{Name: "e", Type: "bytes32[3][2]"},
|
||||
},
|
||||
struct {
|
||||
A int64
|
||||
B *big.Int
|
||||
C *big.Int
|
||||
D bool
|
||||
E [][]common.Hash
|
||||
}{1, big.NewInt(1), big.NewInt(-1), true, [][]common.Hash{{{1}, {2}, {3}}, {{3}, {4}, {5}}}},
|
||||
common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000001" + // struct[a]
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // struct[c]
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[d]
|
||||
"0100000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][0]
|
||||
"0200000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][1]
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[0][2]
|
||||
"0300000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][0]
|
||||
"0400000000000000000000000000000000000000000000000000000000000000" + // struct[e] array[1][1]
|
||||
"0500000000000000000000000000000000000000000000000000000000000000"), // struct[e] array[1][2]
|
||||
},
|
||||
{
|
||||
// dynamic tuple
|
||||
"tuple",
|
||||
[]ArgumentMarshaling{
|
||||
{Name: "a", Type: "string"},
|
||||
{Name: "b", Type: "int64"},
|
||||
{Name: "c", Type: "bytes"},
|
||||
{Name: "d", Type: "string[]"},
|
||||
{Name: "e", Type: "int256[]"},
|
||||
{Name: "f", Type: "address[]"},
|
||||
},
|
||||
struct {
|
||||
FieldA string `abi:"a"` // Test whether abi tag works
|
||||
FieldB int64 `abi:"b"`
|
||||
C []byte
|
||||
D []string
|
||||
E []*big.Int
|
||||
F []common.Address
|
||||
}{"foobar", 1, []byte{1}, []string{"foo", "bar"}, []*big.Int{big.NewInt(1), big.NewInt(-1)}, []common.Address{{1}, {2}}},
|
||||
common.Hex2Bytes("00000000000000000000000000000000000000000000000000000000000000c0" + // struct[a] offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[b]
|
||||
"0000000000000000000000000000000000000000000000000000000000000100" + // struct[c] offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000140" + // struct[d] offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000220" + // struct[e] offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000280" + // struct[f] offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000006" + // struct[a] length
|
||||
"666f6f6261720000000000000000000000000000000000000000000000000000" + // struct[a] "foobar"
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // struct[c] length
|
||||
"0100000000000000000000000000000000000000000000000000000000000000" + // []byte{1}
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[d] length
|
||||
"0000000000000000000000000000000000000000000000000000000000000040" + // foo offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000080" + // bar offset
|
||||
"0000000000000000000000000000000000000000000000000000000000000003" + // foo length
|
||||
"666f6f0000000000000000000000000000000000000000000000000000000000" + // foo
|
||||
"0000000000000000000000000000000000000000000000000000000000000003" + // bar offset
|
||||
"6261720000000000000000000000000000000000000000000000000000000000" + // bar
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[e] length
|
||||
"0000000000000000000000000000000000000000000000000000000000000001" + // 1
|
||||
"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" + // -1
|
||||
"0000000000000000000000000000000000000000000000000000000000000002" + // struct[f] length
|
||||
"0000000000000000000000000100000000000000000000000000000000000000" + // common.Address{1}
|
||||
"0000000000000000000000000200000000000000000000000000000000000000"), // common.Address{2}
|
||||
},
|
||||
{
|
||||
// nested tuple
|
||||
"tuple",
|
||||
[]ArgumentMarshaling{
|
||||
{Name: "a", Type: "tuple", Components: []ArgumentMarshaling{{Name: "a", Type: "uint256"}, {Name: "b", Type: "uint256[]"}}},
|
||||
{Name: "b", Type: "int256[]"},
|
||||
},
|
||||
struct {
|
||||
A struct {
|
||||
FieldA *big.Int `abi:"a"`
|
||||
B []*big.Int
|
||||
}
|
||||
B []*big.Int
|
||||
}{
|
||||
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)
|
||||
}
|
||||
|
||||
if !bytes.Equal(output, test.output) {
|
||||
t.Errorf("%d failed. Expected bytes: '%x' Got: '%x'", i, test.output, output)
|
||||
t.Errorf("input %d for typ: %v failed. Expected bytes: '%x' Got: '%x'", i, typ.String(), test.output, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -406,6 +693,59 @@ func TestMethodPack(t *testing.T) {
|
||||
if !bytes.Equal(packed, sig) {
|
||||
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,28 +223,82 @@ 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
|
||||
}
|
||||
|
||||
if t.T == SliceTy || t.T == ArrayTy {
|
||||
var packed []byte
|
||||
switch t.T {
|
||||
case SliceTy, ArrayTy:
|
||||
var ret []byte
|
||||
|
||||
if t.requiresLengthPrefix() {
|
||||
// append length
|
||||
ret = append(ret, packNum(reflect.ValueOf(v.Len()))...)
|
||||
}
|
||||
|
||||
// calculate offset if any
|
||||
offset := 0
|
||||
offsetReq := isDynamicType(*t.Elem)
|
||||
if offsetReq {
|
||||
offset = getTypeSize(*t.Elem) * v.Len()
|
||||
}
|
||||
var tail []byte
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
val, err := t.Elem.pack(v.Index(i))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
packed = append(packed, val...)
|
||||
if !offsetReq {
|
||||
ret = append(ret, val...)
|
||||
continue
|
||||
}
|
||||
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
||||
offset += len(val)
|
||||
tail = append(tail, val...)
|
||||
}
|
||||
if t.T == SliceTy {
|
||||
return packBytesSlice(packed, v.Len()), nil
|
||||
} else if t.T == ArrayTy {
|
||||
return packed, nil
|
||||
return append(ret, tail...), nil
|
||||
case TupleTy:
|
||||
// (T1,...,Tk) for k >= 0 and any types T1, …, Tk
|
||||
// enc(X) = head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(k))
|
||||
// where X = (X(1), ..., X(k)) and head and tail are defined for Ti being a static
|
||||
// type as
|
||||
// head(X(i)) = enc(X(i)) and tail(X(i)) = "" (the empty string)
|
||||
// and as
|
||||
// head(X(i)) = enc(len(head(X(1)) ... head(X(k)) tail(X(1)) ... tail(X(i-1))))
|
||||
// tail(X(i)) = enc(X(i))
|
||||
// otherwise, i.e. if Ti is a dynamic type.
|
||||
fieldmap, err := mapArgNamesToStructFields(t.TupleRawNames, v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Calculate prefix occupied size.
|
||||
offset := 0
|
||||
for _, elem := range t.TupleElems {
|
||||
offset += getTypeSize(*elem)
|
||||
}
|
||||
var ret, tail []byte
|
||||
for i, elem := range t.TupleElems {
|
||||
field := v.FieldByName(fieldmap[t.TupleRawNames[i]])
|
||||
if !field.IsValid() {
|
||||
return nil, fmt.Errorf("field %s for tuple not found in the given struct", t.TupleRawNames[i])
|
||||
}
|
||||
val, err := elem.pack(field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if isDynamicType(*elem) {
|
||||
ret = append(ret, packNum(reflect.ValueOf(offset))...)
|
||||
tail = append(tail, val...)
|
||||
offset += len(val)
|
||||
} else {
|
||||
ret = append(ret, val...)
|
||||
}
|
||||
}
|
||||
return append(ret, tail...), nil
|
||||
|
||||
default:
|
||||
return packElement(t, v), nil
|
||||
}
|
||||
return packElement(t, v), nil
|
||||
}
|
||||
|
||||
// requireLengthPrefix returns whether the type requires any sort of length
|
||||
@@ -207,3 +306,47 @@ func (t Type) pack(v reflect.Value) ([]byte, error) {
|
||||
func (t Type) requiresLengthPrefix() bool {
|
||||
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy
|
||||
}
|
||||
|
||||
// isDynamicType returns true if the type is dynamic.
|
||||
// The following types are called “dynamic”:
|
||||
// * bytes
|
||||
// * string
|
||||
// * T[] for any T
|
||||
// * T[k] for any dynamic T and any k >= 0
|
||||
// * (T1,...,Tk) if Ti is dynamic for some 1 <= i <= k
|
||||
func isDynamicType(t Type) bool {
|
||||
if t.T == TupleTy {
|
||||
for _, elem := range t.TupleElems {
|
||||
if isDynamicType(*elem) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
return t.T == StringTy || t.T == BytesTy || t.T == SliceTy || (t.T == ArrayTy && isDynamicType(*t.Elem))
|
||||
}
|
||||
|
||||
// getTypeSize returns the size that this type needs to occupy.
|
||||
// We distinguish static and dynamic types. Static types are encoded in-place
|
||||
// and dynamic types are encoded at a separately allocated location after the
|
||||
// current block.
|
||||
// So for a static variable, the size returned represents the size that the
|
||||
// variable actually occupies.
|
||||
// For a dynamic variable, the returned size is fixed 32 bytes, which is used
|
||||
// to store the location reference for actual value storage.
|
||||
func getTypeSize(t Type) int {
|
||||
if t.T == ArrayTy && !isDynamicType(*t.Elem) {
|
||||
// Recursively calculate type size if it is a nested array
|
||||
if t.Elem.T == ArrayTy {
|
||||
return t.Size * getTypeSize(*t.Elem)
|
||||
}
|
||||
return t.Size * 32
|
||||
} else if t.T == TupleTy && !isDynamicType(t) {
|
||||
total := 0
|
||||
for _, elem := range t.TupleElems {
|
||||
total += getTypeSize(*elem)
|
||||
}
|
||||
return total
|
||||
}
|
||||
return 32
|
||||
}
|
||||
|
@@ -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{
|
||||
{
|
||||
|
@@ -265,7 +265,10 @@ func (ac *accountCache) scanAccounts() error {
|
||||
case (addr == common.Address{}):
|
||||
log.Debug("Failed to decode keystore key", "path", path, "err", "missing or zero address")
|
||||
default:
|
||||
return &accounts.Account{Address: addr, URL: accounts.URL{Scheme: KeyStoreScheme, Path: path}}
|
||||
return &accounts.Account{
|
||||
Address: addr,
|
||||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: path},
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -171,7 +171,10 @@ func storeNewKey(ks keyStore, rand io.Reader, auth string) (*Key, accounts.Accou
|
||||
if err != nil {
|
||||
return nil, accounts.Account{}, err
|
||||
}
|
||||
a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))}}
|
||||
a := accounts.Account{
|
||||
Address: key.Address,
|
||||
URL: accounts.URL{Scheme: KeyStoreScheme, Path: ks.JoinPath(keyFileName(key.Address))},
|
||||
}
|
||||
if err := ks.StoreKey(a.URL.Path, key, auth); err != nil {
|
||||
zeroKey(key.PrivateKey)
|
||||
return nil, a, err
|
||||
@@ -224,5 +227,6 @@ func toISO8601(t time.Time) string {
|
||||
} else {
|
||||
tz = fmt.Sprintf("%03d00", offset/3600)
|
||||
}
|
||||
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
|
||||
return fmt.Sprintf("%04d-%02d-%02dT%02d-%02d-%02d.%09d%s",
|
||||
t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second(), t.Nanosecond(), tz)
|
||||
}
|
||||
|
@@ -233,6 +233,7 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
|
||||
PrivateKey: key,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
|
||||
if cryptoJson.Cipher != "aes-128-ctr" {
|
||||
return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
|
@@ -38,7 +38,13 @@ func importPreSaleKey(keyStore keyStore, keyJSON []byte, password string) (accou
|
||||
return accounts.Account{}, nil, err
|
||||
}
|
||||
key.Id = uuid.NewRandom()
|
||||
a := accounts.Account{Address: key.Address, URL: accounts.URL{Scheme: KeyStoreScheme, Path: keyStore.JoinPath(keyFileName(key.Address))}}
|
||||
a := accounts.Account{
|
||||
Address: key.Address,
|
||||
URL: accounts.URL{
|
||||
Scheme: KeyStoreScheme,
|
||||
Path: keyStore.JoinPath(keyFileName(key.Address)),
|
||||
},
|
||||
}
|
||||
err = keyStore.StoreKey(a.URL.Path, key, password)
|
||||
return a, key, err
|
||||
}
|
||||
|
@@ -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
|
||||
|
||||
/*
|
||||
|
@@ -56,13 +56,13 @@ const ExternalAPIVersion = "4.0.0"
|
||||
const InternalAPIVersion = "3.0.0"
|
||||
|
||||
const legalWarning = `
|
||||
WARNING!
|
||||
WARNING!
|
||||
|
||||
Clef is alpha software, and not yet publically released. This software has _not_ been audited, and there
|
||||
are no guarantees about the workings of this software. It may contain severe flaws. You should not use this software
|
||||
unless you agree to take full responsibility for doing so, and know what you are doing.
|
||||
unless you agree to take full responsibility for doing so, and know what you are doing.
|
||||
|
||||
TLDR; THIS IS NOT PRODUCTION-READY SOFTWARE!
|
||||
TLDR; THIS IS NOT PRODUCTION-READY SOFTWARE!
|
||||
|
||||
`
|
||||
|
||||
@@ -136,7 +136,7 @@ var (
|
||||
configdirFlag,
|
||||
},
|
||||
Description: `
|
||||
The init command generates a master seed which Clef can use to store credentials and data needed for
|
||||
The init command generates a master seed which Clef can use to store credentials and data needed for
|
||||
the rule-engine to work.`,
|
||||
}
|
||||
attestCommand = cli.Command{
|
||||
@@ -150,10 +150,10 @@ the rule-engine to work.`,
|
||||
signerSecretFlag,
|
||||
},
|
||||
Description: `
|
||||
The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of
|
||||
incoming requests.
|
||||
The attest command stores the sha256 of the rule.js-file that you want to use for automatic processing of
|
||||
incoming requests.
|
||||
|
||||
Whenever you make an edit to the rule file, you need to use attestation to tell
|
||||
Whenever you make an edit to the rule file, you need to use attestation to tell
|
||||
Clef that the file is 'safe' to execute.`,
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ Clef that the file is 'safe' to execute.`,
|
||||
signerSecretFlag,
|
||||
},
|
||||
Description: `
|
||||
The setpw command stores a password for a given address (keyfile). If you enter a blank passphrase, it will
|
||||
The setpw command stores a password for a given address (keyfile). If you enter a blank passphrase, it will
|
||||
remove any stored credential for that address (keyfile)
|
||||
`,
|
||||
}
|
||||
@@ -258,12 +258,12 @@ func initializeSecrets(c *cli.Context) error {
|
||||
}
|
||||
fmt.Printf("A master seed has been generated into %s\n", location)
|
||||
fmt.Printf(`
|
||||
This is required to be able to store credentials, such as :
|
||||
This is required to be able to store credentials, such as :
|
||||
* Passwords for keystores (used by rule engine)
|
||||
* Storage for javascript rules
|
||||
* Hash of rule-file
|
||||
|
||||
You should treat that file with utmost secrecy, and make a backup of it.
|
||||
You should treat that file with utmost secrecy, and make a backup of it.
|
||||
NOTE: This file does not contain your accounts. Those need to be backed up separately!
|
||||
|
||||
`)
|
||||
|
@@ -89,7 +89,7 @@ func runCmd(ctx *cli.Context) error {
|
||||
genesisConfig *core.Genesis
|
||||
)
|
||||
if ctx.GlobalBool(MachineFlag.Name) {
|
||||
tracer = NewJSONLogger(logconfig, os.Stdout)
|
||||
tracer = vm.NewJSONLogger(logconfig, os.Stdout)
|
||||
} else if ctx.GlobalBool(DebugFlag.Name) {
|
||||
debugLogger = vm.NewStructLogger(logconfig)
|
||||
tracer = debugLogger
|
||||
@@ -206,6 +206,7 @@ func runCmd(ctx *cli.Context) error {
|
||||
execTime := time.Since(tstart)
|
||||
|
||||
if ctx.GlobalBool(DumpFlag.Name) {
|
||||
statedb.Commit(true)
|
||||
statedb.IntermediateRoot(true)
|
||||
fmt.Println(string(statedb.Dump()))
|
||||
}
|
||||
|
@@ -68,7 +68,7 @@ func stateTestCmd(ctx *cli.Context) error {
|
||||
)
|
||||
switch {
|
||||
case ctx.GlobalBool(MachineFlag.Name):
|
||||
tracer = NewJSONLogger(config, os.Stderr)
|
||||
tracer = vm.NewJSONLogger(config, os.Stderr)
|
||||
|
||||
case ctx.GlobalBool(DebugFlag.Name):
|
||||
debugger = vm.NewStructLogger(config)
|
||||
|
@@ -256,7 +256,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
|
||||
}
|
||||
for _, boot := range enodes {
|
||||
old, err := enode.ParseV4(boot.String())
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
stack.Server().AddPeer(old)
|
||||
}
|
||||
}
|
||||
@@ -579,7 +579,7 @@ func (f *faucet) loop() {
|
||||
go func() {
|
||||
for head := range update {
|
||||
// New chain head arrived, query the current stats and stream to clients
|
||||
timestamp := time.Unix(head.Time.Int64(), 0)
|
||||
timestamp := time.Unix(int64(head.Time), 0)
|
||||
if time.Since(timestamp) > time.Hour {
|
||||
log.Warn("Skipping faucet refresh, head too old", "number", head.Number, "hash", head.Hash(), "age", common.PrettyAge(timestamp))
|
||||
continue
|
||||
|
@@ -20,7 +20,7 @@ import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"reflect"
|
||||
"unicode"
|
||||
@@ -152,7 +152,9 @@ func enableWhisper(ctx *cli.Context) bool {
|
||||
|
||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
stack, cfg := makeConfigNode(ctx)
|
||||
|
||||
if ctx.GlobalIsSet(utils.ConstantinopleOverrideFlag.Name) {
|
||||
cfg.Eth.ConstantinopleOverride = new(big.Int).SetUint64(ctx.GlobalUint64(utils.ConstantinopleOverrideFlag.Name))
|
||||
}
|
||||
utils.RegisterEthService(stack, &cfg.Eth)
|
||||
|
||||
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
|
||||
@@ -195,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 (
|
||||
@@ -87,6 +87,7 @@ var (
|
||||
utils.LightServFlag,
|
||||
utils.LightPeersFlag,
|
||||
utils.LightKDFFlag,
|
||||
utils.WhitelistFlag,
|
||||
utils.CacheFlag,
|
||||
utils.CacheDatabaseFlag,
|
||||
utils.CacheTrieFlag,
|
||||
@@ -120,10 +121,10 @@ var (
|
||||
utils.DeveloperPeriodFlag,
|
||||
utils.TestnetFlag,
|
||||
utils.RinkebyFlag,
|
||||
utils.GoerliFlag,
|
||||
utils.VMEnableDebugFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.RPCVirtualHostsFlag,
|
||||
utils.ConstantinopleOverrideFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
utils.MetricsEnabledFlag,
|
||||
utils.FakePoWFlag,
|
||||
@@ -147,6 +148,7 @@ var (
|
||||
utils.WSAllowedOriginsFlag,
|
||||
utils.IPCDisabledFlag,
|
||||
utils.IPCPathFlag,
|
||||
utils.RPCGlobalGasCap,
|
||||
}
|
||||
|
||||
whisperFlags = []cli.Flag{
|
||||
@@ -162,7 +164,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,
|
||||
@@ -81,6 +82,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
utils.LightServFlag,
|
||||
utils.LightPeersFlag,
|
||||
utils.LightKDFFlag,
|
||||
utils.WhitelistFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -151,6 +153,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
utils.RPCListenAddrFlag,
|
||||
utils.RPCPortFlag,
|
||||
utils.RPCApiFlag,
|
||||
utils.RPCGlobalGasCap,
|
||||
utils.WSEnabledFlag,
|
||||
utils.WSListenAddrFlag,
|
||||
utils.WSPortFlag,
|
||||
@@ -228,7 +231,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
utils.MetricsInfluxDBDatabaseFlag,
|
||||
utils.MetricsInfluxDBUsernameFlag,
|
||||
utils.MetricsInfluxDBPasswordFlag,
|
||||
utils.MetricsInfluxDBHostTagFlag,
|
||||
utils.MetricsInfluxDBTagsFlag,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@@ -20,35 +20,41 @@ import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
math2 "github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// cppEthereumGenesisSpec represents the genesis specification format used by the
|
||||
// alethGenesisSpec represents the genesis specification format used by the
|
||||
// C++ Ethereum implementation.
|
||||
type cppEthereumGenesisSpec struct {
|
||||
type alethGenesisSpec struct {
|
||||
SealEngine string `json:"sealEngine"`
|
||||
Params struct {
|
||||
AccountStartNonce hexutil.Uint64 `json:"accountStartNonce"`
|
||||
HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"`
|
||||
EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"`
|
||||
EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"`
|
||||
ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"`
|
||||
ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"`
|
||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||
ChainID hexutil.Uint64 `json:"chainID"`
|
||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||
MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"`
|
||||
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
|
||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
|
||||
DurationLimit *hexutil.Big `json:"durationLimit"`
|
||||
BlockReward *hexutil.Big `json:"blockReward"`
|
||||
AccountStartNonce math2.HexOrDecimal64 `json:"accountStartNonce"`
|
||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||
HomesteadForkBlock hexutil.Uint64 `json:"homesteadForkBlock"`
|
||||
DaoHardforkBlock math2.HexOrDecimal64 `json:"daoHardforkBlock"`
|
||||
EIP150ForkBlock hexutil.Uint64 `json:"EIP150ForkBlock"`
|
||||
EIP158ForkBlock hexutil.Uint64 `json:"EIP158ForkBlock"`
|
||||
ByzantiumForkBlock hexutil.Uint64 `json:"byzantiumForkBlock"`
|
||||
ConstantinopleForkBlock hexutil.Uint64 `json:"constantinopleForkBlock"`
|
||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||
MaxGasLimit hexutil.Uint64 `json:"maxGasLimit"`
|
||||
TieBreakingGas bool `json:"tieBreakingGas"`
|
||||
GasLimitBoundDivisor math2.HexOrDecimal64 `json:"gasLimitBoundDivisor"`
|
||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||
DifficultyBoundDivisor *math2.HexOrDecimal256 `json:"difficultyBoundDivisor"`
|
||||
DurationLimit *math2.HexOrDecimal256 `json:"durationLimit"`
|
||||
BlockReward *hexutil.Big `json:"blockReward"`
|
||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||
ChainID hexutil.Uint64 `json:"chainID"`
|
||||
AllowFutureBlocks bool `json:"allowFutureBlocks"`
|
||||
} `json:"params"`
|
||||
|
||||
Genesis struct {
|
||||
@@ -62,57 +68,68 @@ type cppEthereumGenesisSpec struct {
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit"`
|
||||
} `json:"genesis"`
|
||||
|
||||
Accounts map[common.Address]*cppEthereumGenesisSpecAccount `json:"accounts"`
|
||||
Accounts map[common.UnprefixedAddress]*alethGenesisSpecAccount `json:"accounts"`
|
||||
}
|
||||
|
||||
// cppEthereumGenesisSpecAccount is the prefunded genesis account and/or precompiled
|
||||
// alethGenesisSpecAccount is the prefunded genesis account and/or precompiled
|
||||
// contract definition.
|
||||
type cppEthereumGenesisSpecAccount struct {
|
||||
Balance *hexutil.Big `json:"balance"`
|
||||
Nonce uint64 `json:"nonce,omitempty"`
|
||||
Precompiled *cppEthereumGenesisSpecBuiltin `json:"precompiled,omitempty"`
|
||||
type alethGenesisSpecAccount struct {
|
||||
Balance *math2.HexOrDecimal256 `json:"balance"`
|
||||
Nonce uint64 `json:"nonce,omitempty"`
|
||||
Precompiled *alethGenesisSpecBuiltin `json:"precompiled,omitempty"`
|
||||
}
|
||||
|
||||
// cppEthereumGenesisSpecBuiltin is the precompiled contract definition.
|
||||
type cppEthereumGenesisSpecBuiltin struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"`
|
||||
Linear *cppEthereumGenesisSpecLinearPricing `json:"linear,omitempty"`
|
||||
// alethGenesisSpecBuiltin is the precompiled contract definition.
|
||||
type alethGenesisSpecBuiltin struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
StartingBlock hexutil.Uint64 `json:"startingBlock,omitempty"`
|
||||
Linear *alethGenesisSpecLinearPricing `json:"linear,omitempty"`
|
||||
}
|
||||
|
||||
type cppEthereumGenesisSpecLinearPricing struct {
|
||||
type alethGenesisSpecLinearPricing struct {
|
||||
Base uint64 `json:"base"`
|
||||
Word uint64 `json:"word"`
|
||||
}
|
||||
|
||||
// newCppEthereumGenesisSpec converts a go-ethereum genesis block into a Parity specific
|
||||
// newAlethGenesisSpec converts a go-ethereum genesis block into a Aleth-specific
|
||||
// chain specification format.
|
||||
func newCppEthereumGenesisSpec(network string, genesis *core.Genesis) (*cppEthereumGenesisSpec, error) {
|
||||
// Only ethash is currently supported between go-ethereum and cpp-ethereum
|
||||
func newAlethGenesisSpec(network string, genesis *core.Genesis) (*alethGenesisSpec, error) {
|
||||
// Only ethash is currently supported between go-ethereum and aleth
|
||||
if genesis.Config.Ethash == nil {
|
||||
return nil, errors.New("unsupported consensus engine")
|
||||
}
|
||||
// Reconstruct the chain spec in Parity's format
|
||||
spec := &cppEthereumGenesisSpec{
|
||||
// Reconstruct the chain spec in Aleth format
|
||||
spec := &alethGenesisSpec{
|
||||
SealEngine: "Ethash",
|
||||
}
|
||||
// Some defaults
|
||||
spec.Params.AccountStartNonce = 0
|
||||
spec.Params.TieBreakingGas = false
|
||||
spec.Params.AllowFutureBlocks = false
|
||||
spec.Params.DaoHardforkBlock = 0
|
||||
|
||||
spec.Params.HomesteadForkBlock = (hexutil.Uint64)(genesis.Config.HomesteadBlock.Uint64())
|
||||
spec.Params.EIP150ForkBlock = (hexutil.Uint64)(genesis.Config.EIP150Block.Uint64())
|
||||
spec.Params.EIP158ForkBlock = (hexutil.Uint64)(genesis.Config.EIP158Block.Uint64())
|
||||
spec.Params.ByzantiumForkBlock = (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())
|
||||
spec.Params.ConstantinopleForkBlock = (hexutil.Uint64)(math.MaxUint64)
|
||||
|
||||
// Byzantium
|
||||
if num := genesis.Config.ByzantiumBlock; num != nil {
|
||||
spec.setByzantium(num)
|
||||
}
|
||||
// Constantinople
|
||||
if num := genesis.Config.ConstantinopleBlock; num != nil {
|
||||
spec.setConstantinople(num)
|
||||
}
|
||||
|
||||
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||
|
||||
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
||||
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
||||
spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxUint64)
|
||||
spec.Params.MaxGasLimit = (hexutil.Uint64)(math.MaxInt64)
|
||||
spec.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
|
||||
spec.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
|
||||
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
|
||||
spec.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
|
||||
spec.Params.DifficultyBoundDivisor = (*math2.HexOrDecimal256)(params.DifficultyBoundDivisor)
|
||||
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
|
||||
spec.Params.DurationLimit = (*math2.HexOrDecimal256)(params.DurationLimit)
|
||||
spec.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
|
||||
|
||||
spec.Genesis.Nonce = (hexutil.Bytes)(make([]byte, 8))
|
||||
@@ -126,77 +143,109 @@ func newCppEthereumGenesisSpec(network string, genesis *core.Genesis) (*cppEther
|
||||
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
|
||||
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
|
||||
|
||||
spec.Accounts = make(map[common.Address]*cppEthereumGenesisSpecAccount)
|
||||
for address, account := range genesis.Alloc {
|
||||
spec.Accounts[address] = &cppEthereumGenesisSpecAccount{
|
||||
Balance: (*hexutil.Big)(account.Balance),
|
||||
Nonce: account.Nonce,
|
||||
}
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{1})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "ecrecover", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 3000},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{2})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "sha256", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 60, Word: 12},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{3})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "ripemd160", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 600, Word: 120},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{4})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "identity", Linear: &cppEthereumGenesisSpecLinearPricing{Base: 15, Word: 3},
|
||||
spec.setAccount(address, account)
|
||||
}
|
||||
|
||||
spec.setPrecompile(1, &alethGenesisSpecBuiltin{Name: "ecrecover",
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 3000}})
|
||||
spec.setPrecompile(2, &alethGenesisSpecBuiltin{Name: "sha256",
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 60, Word: 12}})
|
||||
spec.setPrecompile(3, &alethGenesisSpecBuiltin{Name: "ripemd160",
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 600, Word: 120}})
|
||||
spec.setPrecompile(4, &alethGenesisSpecBuiltin{Name: "identity",
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 15, Word: 3}})
|
||||
if genesis.Config.ByzantiumBlock != nil {
|
||||
spec.Accounts[common.BytesToAddress([]byte{5})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "modexp", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{6})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "alt_bn128_G1_add", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 500},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{7})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "alt_bn128_G1_mul", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()), Linear: &cppEthereumGenesisSpecLinearPricing{Base: 40000},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{8})].Precompiled = &cppEthereumGenesisSpecBuiltin{
|
||||
Name: "alt_bn128_pairing_product", StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||
}
|
||||
spec.setPrecompile(5, &alethGenesisSpecBuiltin{Name: "modexp",
|
||||
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())})
|
||||
spec.setPrecompile(6, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_add",
|
||||
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 500}})
|
||||
spec.setPrecompile(7, &alethGenesisSpecBuiltin{Name: "alt_bn128_G1_mul",
|
||||
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64()),
|
||||
Linear: &alethGenesisSpecLinearPricing{Base: 40000}})
|
||||
spec.setPrecompile(8, &alethGenesisSpecBuiltin{Name: "alt_bn128_pairing_product",
|
||||
StartingBlock: (hexutil.Uint64)(genesis.Config.ByzantiumBlock.Uint64())})
|
||||
}
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func (spec *alethGenesisSpec) setPrecompile(address byte, data *alethGenesisSpecBuiltin) {
|
||||
if spec.Accounts == nil {
|
||||
spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount)
|
||||
}
|
||||
addr := common.UnprefixedAddress(common.BytesToAddress([]byte{address}))
|
||||
if _, exist := spec.Accounts[addr]; !exist {
|
||||
spec.Accounts[addr] = &alethGenesisSpecAccount{}
|
||||
}
|
||||
spec.Accounts[addr].Precompiled = data
|
||||
}
|
||||
|
||||
func (spec *alethGenesisSpec) setAccount(address common.Address, account core.GenesisAccount) {
|
||||
if spec.Accounts == nil {
|
||||
spec.Accounts = make(map[common.UnprefixedAddress]*alethGenesisSpecAccount)
|
||||
}
|
||||
|
||||
a, exist := spec.Accounts[common.UnprefixedAddress(address)]
|
||||
if !exist {
|
||||
a = &alethGenesisSpecAccount{}
|
||||
spec.Accounts[common.UnprefixedAddress(address)] = a
|
||||
}
|
||||
a.Balance = (*math2.HexOrDecimal256)(account.Balance)
|
||||
a.Nonce = account.Nonce
|
||||
|
||||
}
|
||||
|
||||
func (spec *alethGenesisSpec) setByzantium(num *big.Int) {
|
||||
spec.Params.ByzantiumForkBlock = hexutil.Uint64(num.Uint64())
|
||||
}
|
||||
|
||||
func (spec *alethGenesisSpec) setConstantinople(num *big.Int) {
|
||||
spec.Params.ConstantinopleForkBlock = hexutil.Uint64(num.Uint64())
|
||||
}
|
||||
|
||||
// parityChainSpec is the chain specification format used by Parity.
|
||||
type parityChainSpec struct {
|
||||
Name string `json:"name"`
|
||||
Engine struct {
|
||||
Name string `json:"name"`
|
||||
Datadir string `json:"dataDir"`
|
||||
Engine struct {
|
||||
Ethash struct {
|
||||
Params struct {
|
||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
|
||||
DurationLimit *hexutil.Big `json:"durationLimit"`
|
||||
BlockReward *hexutil.Big `json:"blockReward"`
|
||||
HomesteadTransition uint64 `json:"homesteadTransition"`
|
||||
EIP150Transition uint64 `json:"eip150Transition"`
|
||||
EIP160Transition uint64 `json:"eip160Transition"`
|
||||
EIP161abcTransition uint64 `json:"eip161abcTransition"`
|
||||
EIP161dTransition uint64 `json:"eip161dTransition"`
|
||||
EIP649Reward *hexutil.Big `json:"eip649Reward"`
|
||||
EIP100bTransition uint64 `json:"eip100bTransition"`
|
||||
EIP649Transition uint64 `json:"eip649Transition"`
|
||||
MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
|
||||
DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
|
||||
DurationLimit *hexutil.Big `json:"durationLimit"`
|
||||
BlockReward map[string]string `json:"blockReward"`
|
||||
DifficultyBombDelays map[string]string `json:"difficultyBombDelays"`
|
||||
HomesteadTransition hexutil.Uint64 `json:"homesteadTransition"`
|
||||
EIP100bTransition hexutil.Uint64 `json:"eip100bTransition"`
|
||||
} `json:"params"`
|
||||
} `json:"Ethash"`
|
||||
} `json:"engine"`
|
||||
|
||||
Params struct {
|
||||
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
|
||||
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
|
||||
GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
|
||||
NetworkID hexutil.Uint64 `json:"networkID"`
|
||||
MaxCodeSize uint64 `json:"maxCodeSize"`
|
||||
EIP155Transition uint64 `json:"eip155Transition"`
|
||||
EIP98Transition uint64 `json:"eip98Transition"`
|
||||
EIP86Transition uint64 `json:"eip86Transition"`
|
||||
EIP140Transition uint64 `json:"eip140Transition"`
|
||||
EIP211Transition uint64 `json:"eip211Transition"`
|
||||
EIP214Transition uint64 `json:"eip214Transition"`
|
||||
EIP658Transition uint64 `json:"eip658Transition"`
|
||||
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 {
|
||||
@@ -215,22 +264,22 @@ type parityChainSpec struct {
|
||||
GasLimit hexutil.Uint64 `json:"gasLimit"`
|
||||
} `json:"genesis"`
|
||||
|
||||
Nodes []string `json:"nodes"`
|
||||
Accounts map[common.Address]*parityChainSpecAccount `json:"accounts"`
|
||||
Nodes []string `json:"nodes"`
|
||||
Accounts map[common.UnprefixedAddress]*parityChainSpecAccount `json:"accounts"`
|
||||
}
|
||||
|
||||
// parityChainSpecAccount is the prefunded genesis account and/or precompiled
|
||||
// contract definition.
|
||||
type parityChainSpecAccount struct {
|
||||
Balance *hexutil.Big `json:"balance"`
|
||||
Nonce uint64 `json:"nonce,omitempty"`
|
||||
Balance math2.HexOrDecimal256 `json:"balance"`
|
||||
Nonce math2.HexOrDecimal64 `json:"nonce,omitempty"`
|
||||
Builtin *parityChainSpecBuiltin `json:"builtin,omitempty"`
|
||||
}
|
||||
|
||||
// parityChainSpecBuiltin is the precompiled contract definition.
|
||||
type parityChainSpecBuiltin struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
ActivateAt uint64 `json:"activate_at,omitempty"`
|
||||
ActivateAt math2.HexOrDecimal64 `json:"activate_at,omitempty"`
|
||||
Pricing *parityChainSpecPricing `json:"pricing,omitempty"`
|
||||
}
|
||||
|
||||
@@ -265,34 +314,56 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
|
||||
}
|
||||
// Reconstruct the chain spec in Parity's format
|
||||
spec := &parityChainSpec{
|
||||
Name: network,
|
||||
Nodes: bootnodes,
|
||||
Name: network,
|
||||
Nodes: bootnodes,
|
||||
Datadir: strings.ToLower(network),
|
||||
}
|
||||
spec.Engine.Ethash.Params.BlockReward = make(map[string]string)
|
||||
spec.Engine.Ethash.Params.DifficultyBombDelays = make(map[string]string)
|
||||
// Frontier
|
||||
spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
|
||||
spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
|
||||
spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
|
||||
spec.Engine.Ethash.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
|
||||
spec.Engine.Ethash.Params.HomesteadTransition = genesis.Config.HomesteadBlock.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP150Transition = genesis.Config.EIP150Block.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP160Transition = genesis.Config.EIP155Block.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP161abcTransition = genesis.Config.EIP158Block.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP161dTransition = genesis.Config.EIP158Block.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP649Reward = (*hexutil.Big)(ethash.ByzantiumBlockReward)
|
||||
spec.Engine.Ethash.Params.EIP100bTransition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Engine.Ethash.Params.EIP649Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Engine.Ethash.Params.BlockReward["0x0"] = hexutil.EncodeBig(ethash.FrontierBlockReward)
|
||||
|
||||
// Homestead
|
||||
spec.Engine.Ethash.Params.HomesteadTransition = hexutil.Uint64(genesis.Config.HomesteadBlock.Uint64())
|
||||
|
||||
// Tangerine Whistle : 150
|
||||
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-608.md
|
||||
spec.Params.EIP150Transition = hexutil.Uint64(genesis.Config.EIP150Block.Uint64())
|
||||
|
||||
// Spurious Dragon: 155, 160, 161, 170
|
||||
// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-607.md
|
||||
spec.Params.EIP155Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64())
|
||||
spec.Params.EIP160Transition = hexutil.Uint64(genesis.Config.EIP155Block.Uint64())
|
||||
spec.Params.EIP161abcTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64())
|
||||
spec.Params.EIP161dTransition = hexutil.Uint64(genesis.Config.EIP158Block.Uint64())
|
||||
|
||||
// Byzantium
|
||||
if num := genesis.Config.ByzantiumBlock; num != nil {
|
||||
spec.setByzantium(num)
|
||||
}
|
||||
// Constantinople
|
||||
if num := genesis.Config.ConstantinopleBlock; num != nil {
|
||||
spec.setConstantinople(num)
|
||||
}
|
||||
// ConstantinopleFix (remove eip-1283)
|
||||
if num := genesis.Config.PetersburgBlock; num != nil {
|
||||
spec.setConstantinopleFix(num)
|
||||
}
|
||||
|
||||
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
|
||||
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
|
||||
spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
|
||||
spec.Params.GasLimitBoundDivisor = (math2.HexOrDecimal64)(params.GasLimitBoundDivisor)
|
||||
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||
spec.Params.ChainID = (hexutil.Uint64)(genesis.Config.ChainID.Uint64())
|
||||
spec.Params.MaxCodeSize = params.MaxCodeSize
|
||||
spec.Params.EIP155Transition = genesis.Config.EIP155Block.Uint64()
|
||||
spec.Params.EIP98Transition = math.MaxUint64
|
||||
spec.Params.EIP86Transition = math.MaxUint64
|
||||
spec.Params.EIP140Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Params.EIP211Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Params.EIP214Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
spec.Params.EIP658Transition = genesis.Config.ByzantiumBlock.Uint64()
|
||||
// geth has it set from zero
|
||||
spec.Params.MaxCodeSizeTransition = 0
|
||||
|
||||
// Disable this one
|
||||
spec.Params.EIP98Transition = math.MaxInt64
|
||||
|
||||
spec.Genesis.Seal.Ethereum.Nonce = (hexutil.Bytes)(make([]byte, 8))
|
||||
binary.LittleEndian.PutUint64(spec.Genesis.Seal.Ethereum.Nonce[:], genesis.Nonce)
|
||||
@@ -305,42 +376,81 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
|
||||
spec.Genesis.ExtraData = (hexutil.Bytes)(genesis.ExtraData)
|
||||
spec.Genesis.GasLimit = (hexutil.Uint64)(genesis.GasLimit)
|
||||
|
||||
spec.Accounts = make(map[common.Address]*parityChainSpecAccount)
|
||||
spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount)
|
||||
for address, account := range genesis.Alloc {
|
||||
spec.Accounts[address] = &parityChainSpecAccount{
|
||||
Balance: (*hexutil.Big)(account.Balance),
|
||||
Nonce: account.Nonce,
|
||||
bal := math2.HexOrDecimal256(*account.Balance)
|
||||
|
||||
spec.Accounts[common.UnprefixedAddress(address)] = &parityChainSpecAccount{
|
||||
Balance: bal,
|
||||
Nonce: math2.HexOrDecimal64(account.Nonce),
|
||||
}
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{1})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "ecrecover", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{2})].Builtin = &parityChainSpecBuiltin{
|
||||
spec.setPrecompile(1, &parityChainSpecBuiltin{Name: "ecrecover",
|
||||
Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 3000}}})
|
||||
|
||||
spec.setPrecompile(2, &parityChainSpecBuiltin{
|
||||
Name: "sha256", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 60, Word: 12}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{3})].Builtin = &parityChainSpecBuiltin{
|
||||
})
|
||||
spec.setPrecompile(3, &parityChainSpecBuiltin{
|
||||
Name: "ripemd160", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 600, Word: 120}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{4})].Builtin = &parityChainSpecBuiltin{
|
||||
})
|
||||
spec.setPrecompile(4, &parityChainSpecBuiltin{
|
||||
Name: "identity", Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 15, Word: 3}},
|
||||
}
|
||||
})
|
||||
if genesis.Config.ByzantiumBlock != nil {
|
||||
spec.Accounts[common.BytesToAddress([]byte{5})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "modexp", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{6})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_add", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{7})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_mul", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}},
|
||||
}
|
||||
spec.Accounts[common.BytesToAddress([]byte{8})].Builtin = &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_pairing", ActivateAt: genesis.Config.ByzantiumBlock.Uint64(), Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}},
|
||||
}
|
||||
blnum := math2.HexOrDecimal64(genesis.Config.ByzantiumBlock.Uint64())
|
||||
spec.setPrecompile(5, &parityChainSpecBuiltin{
|
||||
Name: "modexp", ActivateAt: blnum, Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}},
|
||||
})
|
||||
spec.setPrecompile(6, &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_add", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 500}},
|
||||
})
|
||||
spec.setPrecompile(7, &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_mul", ActivateAt: blnum, Pricing: &parityChainSpecPricing{Linear: &parityChainSpecLinearPricing{Base: 40000}},
|
||||
})
|
||||
spec.setPrecompile(8, &parityChainSpecBuiltin{
|
||||
Name: "alt_bn128_pairing", ActivateAt: blnum, Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}},
|
||||
})
|
||||
}
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func (spec *parityChainSpec) setPrecompile(address byte, data *parityChainSpecBuiltin) {
|
||||
if spec.Accounts == nil {
|
||||
spec.Accounts = make(map[common.UnprefixedAddress]*parityChainSpecAccount)
|
||||
}
|
||||
a := common.UnprefixedAddress(common.BytesToAddress([]byte{address}))
|
||||
if _, exist := spec.Accounts[a]; !exist {
|
||||
spec.Accounts[a] = &parityChainSpecAccount{}
|
||||
}
|
||||
spec.Accounts[a].Builtin = data
|
||||
}
|
||||
|
||||
func (spec *parityChainSpec) setByzantium(num *big.Int) {
|
||||
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ByzantiumBlockReward)
|
||||
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(3000000)
|
||||
n := hexutil.Uint64(num.Uint64())
|
||||
spec.Engine.Ethash.Params.EIP100bTransition = n
|
||||
spec.Params.EIP140Transition = n
|
||||
spec.Params.EIP211Transition = n
|
||||
spec.Params.EIP214Transition = n
|
||||
spec.Params.EIP658Transition = n
|
||||
}
|
||||
|
||||
func (spec *parityChainSpec) setConstantinople(num *big.Int) {
|
||||
spec.Engine.Ethash.Params.BlockReward[hexutil.EncodeBig(num)] = hexutil.EncodeBig(ethash.ConstantinopleBlockReward)
|
||||
spec.Engine.Ethash.Params.DifficultyBombDelays[hexutil.EncodeBig(num)] = hexutil.EncodeUint64(2000000)
|
||||
n := hexutil.Uint64(num.Uint64())
|
||||
spec.Params.EIP145Transition = n
|
||||
spec.Params.EIP1014Transition = n
|
||||
spec.Params.EIP1052Transition = n
|
||||
spec.Params.EIP1283Transition = n
|
||||
}
|
||||
|
||||
func (spec *parityChainSpec) setConstantinopleFix(num *big.Int) {
|
||||
spec.Params.EIP1283DisableTransition = hexutil.Uint64(num.Uint64())
|
||||
}
|
||||
|
||||
// pyEthereumGenesisSpec represents the genesis specification format used by the
|
||||
// Python Ethereum implementation.
|
||||
type pyEthereumGenesisSpec struct {
|
||||
|
109
cmd/puppeth/genesis_test.go
Normal file
109
cmd/puppeth/genesis_test.go
Normal file
@@ -0,0 +1,109 @@
|
||||
// Copyright 2018 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
)
|
||||
|
||||
// Tests the go-ethereum to Aleth chainspec conversion for the Stureby testnet.
|
||||
func TestAlethSturebyConverter(t *testing.T) {
|
||||
blob, err := ioutil.ReadFile("testdata/stureby_geth.json")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %v", err)
|
||||
}
|
||||
var genesis core.Genesis
|
||||
if err := json.Unmarshal(blob, &genesis); err != nil {
|
||||
t.Fatalf("failed parsing genesis: %v", err)
|
||||
}
|
||||
spec, err := newAlethGenesisSpec("stureby", &genesis)
|
||||
if err != nil {
|
||||
t.Fatalf("failed creating chainspec: %v", err)
|
||||
}
|
||||
|
||||
expBlob, err := ioutil.ReadFile("testdata/stureby_aleth.json")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %v", err)
|
||||
}
|
||||
expspec := &alethGenesisSpec{}
|
||||
if err := json.Unmarshal(expBlob, expspec); err != nil {
|
||||
t.Fatalf("failed parsing genesis: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(expspec, spec) {
|
||||
t.Errorf("chainspec mismatch")
|
||||
c := spew.ConfigState{
|
||||
DisablePointerAddresses: true,
|
||||
SortKeys: true,
|
||||
}
|
||||
exp := strings.Split(c.Sdump(expspec), "\n")
|
||||
got := strings.Split(c.Sdump(spec), "\n")
|
||||
for i := 0; i < len(exp) && i < len(got); i++ {
|
||||
if exp[i] != got[i] {
|
||||
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Tests the go-ethereum to Parity chainspec conversion for the Stureby testnet.
|
||||
func TestParitySturebyConverter(t *testing.T) {
|
||||
blob, err := ioutil.ReadFile("testdata/stureby_geth.json")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %v", err)
|
||||
}
|
||||
var genesis core.Genesis
|
||||
if err := json.Unmarshal(blob, &genesis); err != nil {
|
||||
t.Fatalf("failed parsing genesis: %v", err)
|
||||
}
|
||||
spec, err := newParityChainSpec("Stureby", &genesis, []string{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed creating chainspec: %v", err)
|
||||
}
|
||||
|
||||
expBlob, err := ioutil.ReadFile("testdata/stureby_parity.json")
|
||||
if err != nil {
|
||||
t.Fatalf("could not read file: %v", err)
|
||||
}
|
||||
expspec := &parityChainSpec{}
|
||||
if err := json.Unmarshal(expBlob, expspec); err != nil {
|
||||
t.Fatalf("failed parsing genesis: %v", err)
|
||||
}
|
||||
expspec.Nodes = []string{}
|
||||
|
||||
if !reflect.DeepEqual(expspec, spec) {
|
||||
t.Errorf("chainspec mismatch")
|
||||
c := spew.ConfigState{
|
||||
DisablePointerAddresses: true,
|
||||
SortKeys: true,
|
||||
}
|
||||
exp := strings.Split(c.Sdump(expspec), "\n")
|
||||
got := strings.Split(c.Sdump(spec), "\n")
|
||||
for i := 0; i < len(exp) && i < len(got); i++ {
|
||||
if exp[i] != got[i] {
|
||||
fmt.Printf("got: %v\nexp: %v\n", exp[i], got[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -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()
|
||||
|
||||
@@ -640,7 +641,7 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
|
||||
files[filepath.Join(workdir, network+".json")] = genesis
|
||||
|
||||
if conf.Genesis.Config.Ethash != nil {
|
||||
cppSpec, err := newCppEthereumGenesisSpec(network, conf.Genesis)
|
||||
cppSpec, err := newAlethGenesisSpec(network, conf.Genesis)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@@ -43,7 +43,8 @@ version: '2'
|
||||
services:
|
||||
ethstats:
|
||||
build: .
|
||||
image: {{.Network}}/ethstats{{if not .VHost}}
|
||||
image: {{.Network}}/ethstats
|
||||
container_name: {{.Network}}_ethstats_1{{if not .VHost}}
|
||||
ports:
|
||||
- "{{.Port}}:3000"{{end}}
|
||||
environment:
|
||||
|
@@ -77,6 +77,7 @@ services:
|
||||
explorer:
|
||||
build: .
|
||||
image: {{.Network}}/explorer
|
||||
container_name: {{.Network}}_explorer_1
|
||||
ports:
|
||||
- "{{.NodePort}}:{{.NodePort}}"
|
||||
- "{{.NodePort}}:{{.NodePort}}/udp"{{if not .VHost}}
|
||||
|
@@ -56,8 +56,10 @@ services:
|
||||
faucet:
|
||||
build: .
|
||||
image: {{.Network}}/faucet
|
||||
container_name: {{.Network}}_faucet_1
|
||||
ports:
|
||||
- "{{.EthPort}}:{{.EthPort}}"{{if not .VHost}}
|
||||
- "{{.EthPort}}:{{.EthPort}}"
|
||||
- "{{.EthPort}}:{{.EthPort}}/udp"{{if not .VHost}}
|
||||
- "{{.ApiPort}}:8080"{{end}}
|
||||
volumes:
|
||||
- {{.Datadir}}:/root/.faucet
|
||||
|
@@ -40,6 +40,7 @@ services:
|
||||
nginx:
|
||||
build: .
|
||||
image: {{.Network}}/nginx
|
||||
container_name: {{.Network}}_nginx_1
|
||||
ports:
|
||||
- "{{.Port}}:80"
|
||||
volumes:
|
||||
|
@@ -55,6 +55,7 @@ services:
|
||||
{{.Type}}:
|
||||
build: .
|
||||
image: {{.Network}}/{{.Type}}
|
||||
container_name: {{.Network}}_{{.Type}}_1
|
||||
ports:
|
||||
- "{{.Port}}:{{.Port}}"
|
||||
- "{{.Port}}:{{.Port}}/udp"
|
||||
|
@@ -57,6 +57,7 @@ services:
|
||||
wallet:
|
||||
build: .
|
||||
image: {{.Network}}/wallet
|
||||
container_name: {{.Network}}_wallet_1
|
||||
ports:
|
||||
- "{{.NodePort}}:{{.NodePort}}"
|
||||
- "{{.NodePort}}:{{.NodePort}}/udp"
|
||||
|
@@ -43,18 +43,23 @@ func main() {
|
||||
Usage: "log level to emit to the screen",
|
||||
},
|
||||
}
|
||||
app.Action = func(c *cli.Context) error {
|
||||
app.Before = func(c *cli.Context) error {
|
||||
// Set up the logger to print everything and the random generator
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(c.Int("loglevel")), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
|
||||
network := c.String("network")
|
||||
if strings.Contains(network, " ") || strings.Contains(network, "-") {
|
||||
log.Crit("No spaces or hyphens allowed in network name")
|
||||
}
|
||||
// Start the wizard and relinquish control
|
||||
makeWizard(c.String("network")).run()
|
||||
return nil
|
||||
}
|
||||
app.Action = runWizard
|
||||
app.Run(os.Args)
|
||||
}
|
||||
|
||||
// runWizard start the wizard and relinquish control to it.
|
||||
func runWizard(c *cli.Context) error {
|
||||
network := c.String("network")
|
||||
if strings.Contains(network, " ") || strings.Contains(network, "-") || strings.ToLower(network) != network {
|
||||
log.Crit("No spaces, hyphens or capital letters allowed in network name")
|
||||
}
|
||||
makeWizard(c.String("network")).run()
|
||||
return nil
|
||||
}
|
||||
|
112
cmd/puppeth/testdata/stureby_aleth.json
vendored
Normal file
112
cmd/puppeth/testdata/stureby_aleth.json
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
{
|
||||
"sealEngine":"Ethash",
|
||||
"params":{
|
||||
"accountStartNonce":"0x00",
|
||||
"maximumExtraDataSize":"0x20",
|
||||
"homesteadForkBlock":"0x2710",
|
||||
"daoHardforkBlock":"0x00",
|
||||
"EIP150ForkBlock":"0x3a98",
|
||||
"EIP158ForkBlock":"0x59d8",
|
||||
"byzantiumForkBlock":"0x7530",
|
||||
"constantinopleForkBlock":"0x9c40",
|
||||
"minGasLimit":"0x1388",
|
||||
"maxGasLimit":"0x7fffffffffffffff",
|
||||
"tieBreakingGas":false,
|
||||
"gasLimitBoundDivisor":"0x0400",
|
||||
"minimumDifficulty":"0x20000",
|
||||
"difficultyBoundDivisor":"0x0800",
|
||||
"durationLimit":"0x0d",
|
||||
"blockReward":"0x4563918244F40000",
|
||||
"networkID":"0x4cb2e",
|
||||
"chainID":"0x4cb2e",
|
||||
"allowFutureBlocks":false
|
||||
},
|
||||
"genesis":{
|
||||
"nonce":"0x0000000000000000",
|
||||
"difficulty":"0x20000",
|
||||
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"author":"0x0000000000000000000000000000000000000000",
|
||||
"timestamp":"0x59a4e76d",
|
||||
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
|
||||
"gasLimit":"0x47b760"
|
||||
},
|
||||
"accounts":{
|
||||
"0000000000000000000000000000000000000001":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"ecrecover",
|
||||
"linear":{
|
||||
"base":3000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000002":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"sha256",
|
||||
"linear":{
|
||||
"base":60,
|
||||
"word":12
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000003":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"ripemd160",
|
||||
"linear":{
|
||||
"base":600,
|
||||
"word":120
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000004":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"identity",
|
||||
"linear":{
|
||||
"base":15,
|
||||
"word":3
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000005":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"modexp",
|
||||
"startingBlock":"0x7530"
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000006":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"alt_bn128_G1_add",
|
||||
"startingBlock":"0x7530",
|
||||
"linear":{
|
||||
"base":500,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000007":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"alt_bn128_G1_mul",
|
||||
"startingBlock":"0x7530",
|
||||
"linear":{
|
||||
"base":40000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000008":{
|
||||
"balance":"1",
|
||||
"precompiled":{
|
||||
"name":"alt_bn128_pairing_product",
|
||||
"startingBlock":"0x7530"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
cmd/puppeth/testdata/stureby_geth.json
vendored
Normal file
47
cmd/puppeth/testdata/stureby_geth.json
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"config": {
|
||||
"ethash":{},
|
||||
"chainId": 314158,
|
||||
"homesteadBlock": 10000,
|
||||
"eip150Block": 15000,
|
||||
"eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"eip155Block": 23000,
|
||||
"eip158Block": 23000,
|
||||
"byzantiumBlock": 30000,
|
||||
"constantinopleBlock": 40000
|
||||
},
|
||||
"nonce": "0x0",
|
||||
"timestamp": "0x59a4e76d",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
|
||||
"gasLimit": "0x47b760",
|
||||
"difficulty": "0x20000",
|
||||
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"alloc": {
|
||||
"0000000000000000000000000000000000000001": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000002": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000003": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000004": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000005": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000006": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000007": {
|
||||
"balance": "0x01"
|
||||
},
|
||||
"0000000000000000000000000000000000000008": {
|
||||
"balance": "0x01"
|
||||
}
|
||||
}
|
||||
}
|
181
cmd/puppeth/testdata/stureby_parity.json
vendored
Normal file
181
cmd/puppeth/testdata/stureby_parity.json
vendored
Normal file
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"name":"Stureby",
|
||||
"dataDir":"stureby",
|
||||
"engine":{
|
||||
"Ethash":{
|
||||
"params":{
|
||||
"minimumDifficulty":"0x20000",
|
||||
"difficultyBoundDivisor":"0x800",
|
||||
"durationLimit":"0xd",
|
||||
"blockReward":{
|
||||
"0x0":"0x4563918244f40000",
|
||||
"0x7530":"0x29a2241af62c0000",
|
||||
"0x9c40":"0x1bc16d674ec80000"
|
||||
},
|
||||
"homesteadTransition":"0x2710",
|
||||
"eip100bTransition":"0x7530",
|
||||
"difficultyBombDelays":{
|
||||
"0x7530":"0x2dc6c0",
|
||||
"0x9c40":"0x1e8480"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"params":{
|
||||
"accountStartNonce":"0x0",
|
||||
"maximumExtraDataSize":"0x20",
|
||||
"gasLimitBoundDivisor":"0x400",
|
||||
"minGasLimit":"0x1388",
|
||||
"networkID":"0x4cb2e",
|
||||
"chainID":"0x4cb2e",
|
||||
"maxCodeSize":"0x6000",
|
||||
"maxCodeSizeTransition":"0x0",
|
||||
"eip98Transition": "0x7fffffffffffffff",
|
||||
"eip150Transition":"0x3a98",
|
||||
"eip160Transition":"0x59d8",
|
||||
"eip161abcTransition":"0x59d8",
|
||||
"eip161dTransition":"0x59d8",
|
||||
"eip155Transition":"0x59d8",
|
||||
"eip140Transition":"0x7530",
|
||||
"eip211Transition":"0x7530",
|
||||
"eip214Transition":"0x7530",
|
||||
"eip658Transition":"0x7530",
|
||||
"eip145Transition":"0x9c40",
|
||||
"eip1014Transition":"0x9c40",
|
||||
"eip1052Transition":"0x9c40",
|
||||
"eip1283Transition":"0x9c40"
|
||||
},
|
||||
"genesis":{
|
||||
"seal":{
|
||||
"ethereum":{
|
||||
"nonce":"0x0000000000000000",
|
||||
"mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||
}
|
||||
},
|
||||
"difficulty":"0x20000",
|
||||
"author":"0x0000000000000000000000000000000000000000",
|
||||
"timestamp":"0x59a4e76d",
|
||||
"parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData":"0x0000000000000000000000000000000000000000000000000000000b4dc0ffee",
|
||||
"gasLimit":"0x47b760"
|
||||
},
|
||||
"nodes":[
|
||||
"enode://dfa7aca3f5b635fbfe7d0b20575f25e40d9e27b4bfbb3cf74364a42023ad9f25c1a4383bcc8cced86ee511a7d03415345a4df05be37f1dff040e4c780699f1c0@168.61.153.255:31303",
|
||||
"enode://ef441b20dd70aeabf0eac35c3b8a2854e5ce04db0e30be9152ea9fd129359dcbb3f803993303ff5781c755dfd7223f3fe43505f583cccb740949407677412ba9@40.74.91.252:31303",
|
||||
"enode://953b5ea1c8987cf46008232a0160324fd00d41320ecf00e23af86ec8f5396b19eb57ddab37c78141be56f62e9077de4f4dfa0747fa768ed8c8531bbfb1046237@40.70.214.166:31303",
|
||||
"enode://276e613dd4b277a66591e565711e6c8bb107f0905248a9f8f8228c1a87992e156e5114bb9937c02824a9d9d25f76340442cf86e2028bf5293cae19904fb2b98e@35.178.251.52:30303",
|
||||
"enode://064c820d41e52ed7d426ac64b60506c2998235bedc7e67cb497c6faf7bb4fc54fe56fc82d0add3180b747c0c4f40a1108a6f84d7d0629ed606d504528e61cc57@3.8.5.3:30303",
|
||||
"enode://90069fdabcc5e684fa5d59430bebbb12755d9362dfe5006a1485b13d71a78a3812d36e74dd7d88e50b51add01e097ea80f16263aeaa4f0230db6c79e2a97e7ca@217.29.191.142:30303",
|
||||
"enode://0aac74b7fd28726275e466acb5e03bc88a95927e9951eb66b5efb239b2f798ada0690853b2f2823fe4efa408f0f3d4dd258430bc952a5ff70677b8625b3e3b14@40.115.33.57:40404",
|
||||
"enode://0b96415a10f835106d83e090a0528eed5e7887e5c802a6d084e9f1993a9d0fc713781e6e4101f6365e9b91259712f291acc0a9e6e667e22023050d602c36fbe2@40.115.33.57:40414"
|
||||
],
|
||||
"accounts":{
|
||||
"0000000000000000000000000000000000000001":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"ecrecover",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":3000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000002":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"sha256",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":60,
|
||||
"word":12
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000003":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"ripemd160",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":600,
|
||||
"word":120
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000004":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"identity",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":15,
|
||||
"word":3
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000005":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"modexp",
|
||||
"activate_at":"0x7530",
|
||||
"pricing":{
|
||||
"modexp":{
|
||||
"divisor":20
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000006":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"alt_bn128_add",
|
||||
"activate_at":"0x7530",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":500,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000007":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"alt_bn128_mul",
|
||||
"activate_at":"0x7530",
|
||||
"pricing":{
|
||||
"linear":{
|
||||
"base":40000,
|
||||
"word":0
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"0000000000000000000000000000000000000008":{
|
||||
"balance":"1",
|
||||
"nonce":"0",
|
||||
"builtin":{
|
||||
"name":"alt_bn128_pairing",
|
||||
"activate_at":"0x7530",
|
||||
"pricing":{
|
||||
"alt_bn128_pairing":{
|
||||
"base":100000,
|
||||
"pair":80000
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -23,6 +23,7 @@ import (
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
@@ -118,6 +119,47 @@ func (w *wizard) readDefaultString(def string) string {
|
||||
return def
|
||||
}
|
||||
|
||||
// readDefaultYesNo reads a single line from stdin, trimming if from spaces and
|
||||
// interpreting it as a 'yes' or a 'no'. If an empty line is entered, the default
|
||||
// value is returned.
|
||||
func (w *wizard) readDefaultYesNo(def bool) bool {
|
||||
for {
|
||||
fmt.Printf("> ")
|
||||
text, err := w.in.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Crit("Failed to read user input", "err", err)
|
||||
}
|
||||
if text = strings.ToLower(strings.TrimSpace(text)); text == "" {
|
||||
return def
|
||||
}
|
||||
if text == "y" || text == "yes" {
|
||||
return true
|
||||
}
|
||||
if text == "n" || text == "no" {
|
||||
return false
|
||||
}
|
||||
log.Error("Invalid input, expected 'y', 'yes', 'n', 'no' or empty")
|
||||
}
|
||||
}
|
||||
|
||||
// readURL reads a single line from stdin, trimming if from spaces and trying to
|
||||
// interpret it as a URL (http, https or file).
|
||||
func (w *wizard) readURL() *url.URL {
|
||||
for {
|
||||
fmt.Printf("> ")
|
||||
text, err := w.in.ReadString('\n')
|
||||
if err != nil {
|
||||
log.Crit("Failed to read user input", "err", err)
|
||||
}
|
||||
uri, err := url.Parse(strings.TrimSpace(text))
|
||||
if err != nil {
|
||||
log.Error("Invalid input, expected URL", "err", err)
|
||||
continue
|
||||
}
|
||||
return uri
|
||||
}
|
||||
}
|
||||
|
||||
// readInt reads a single line from stdin, trimming if from spaces, enforcing it
|
||||
// to parse into an integer.
|
||||
func (w *wizard) readInt() int {
|
||||
|
@@ -137,14 +137,14 @@ func (w *wizard) deployDashboard() {
|
||||
if w.conf.ethstats != "" {
|
||||
fmt.Println()
|
||||
fmt.Println("Include ethstats secret on dashboard (y/n)? (default = yes)")
|
||||
infos.trusted = w.readDefaultString("y") == "y"
|
||||
infos.trusted = w.readDefaultYesNo(true)
|
||||
}
|
||||
// Try to deploy the dashboard container on the host
|
||||
nocache := false
|
||||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the dashboard be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployDashboard(client, w.network, &w.conf, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy dashboard container", "err", err)
|
||||
|
@@ -67,11 +67,11 @@ func (w *wizard) deployEthstats() {
|
||||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Keep existing IP %v blacklist (y/n)? (default = yes)\n", infos.banned)
|
||||
if w.readDefaultString("y") != "y" {
|
||||
if !w.readDefaultYesNo(true) {
|
||||
// The user might want to clear the entire list, although generally probably not
|
||||
fmt.Println()
|
||||
fmt.Printf("Clear out blacklist and start over (y/n)? (default = no)\n")
|
||||
if w.readDefaultString("n") != "n" {
|
||||
if w.readDefaultYesNo(false) {
|
||||
infos.banned = nil
|
||||
}
|
||||
// Offer the user to explicitly add/remove certain IP addresses
|
||||
@@ -106,7 +106,7 @@ func (w *wizard) deployEthstats() {
|
||||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the ethstats be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
trusted := make([]string, 0, len(w.servers))
|
||||
for _, client := range w.servers {
|
||||
|
@@ -100,7 +100,7 @@ func (w *wizard) deployExplorer() {
|
||||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the explorer be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployExplorer(client, w.network, chain, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy explorer container", "err", err)
|
||||
|
@@ -81,7 +81,7 @@ func (w *wizard) deployFaucet() {
|
||||
if infos.captchaToken != "" {
|
||||
fmt.Println()
|
||||
fmt.Println("Reuse previous reCaptcha API authorization (y/n)? (default = yes)")
|
||||
if w.readDefaultString("y") != "y" {
|
||||
if !w.readDefaultYesNo(true) {
|
||||
infos.captchaToken, infos.captchaSecret = "", ""
|
||||
}
|
||||
}
|
||||
@@ -89,7 +89,7 @@ func (w *wizard) deployFaucet() {
|
||||
// No previous authorization (or old one discarded)
|
||||
fmt.Println()
|
||||
fmt.Println("Enable reCaptcha protection against robots (y/n)? (default = no)")
|
||||
if w.readDefaultString("n") == "n" {
|
||||
if !w.readDefaultYesNo(false) {
|
||||
log.Warn("Users will be able to requests funds via automated scripts")
|
||||
} else {
|
||||
// Captcha protection explicitly requested, read the site and secret keys
|
||||
@@ -132,7 +132,7 @@ func (w *wizard) deployFaucet() {
|
||||
} else {
|
||||
fmt.Println()
|
||||
fmt.Printf("Reuse previous (%s) funding account (y/n)? (default = yes)\n", key.Address.Hex())
|
||||
if w.readDefaultString("y") != "y" {
|
||||
if !w.readDefaultYesNo(true) {
|
||||
infos.node.keyJSON, infos.node.keyPass = "", ""
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ func (w *wizard) deployFaucet() {
|
||||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployFaucet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy faucet container", "err", err)
|
||||
|
@@ -20,9 +20,13 @@ import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -40,11 +44,12 @@ func (w *wizard) makeGenesis() {
|
||||
Difficulty: big.NewInt(524288),
|
||||
Alloc: make(core.GenesisAlloc),
|
||||
Config: ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(1),
|
||||
EIP150Block: big.NewInt(2),
|
||||
EIP155Block: big.NewInt(3),
|
||||
EIP158Block: big.NewInt(3),
|
||||
ByzantiumBlock: big.NewInt(4),
|
||||
HomesteadBlock: big.NewInt(1),
|
||||
EIP150Block: big.NewInt(2),
|
||||
EIP155Block: big.NewInt(3),
|
||||
EIP158Block: big.NewInt(3),
|
||||
ByzantiumBlock: big.NewInt(4),
|
||||
ConstantinopleBlock: big.NewInt(5),
|
||||
},
|
||||
}
|
||||
// Figure out which consensus engine to choose
|
||||
@@ -114,9 +119,13 @@ func (w *wizard) makeGenesis() {
|
||||
}
|
||||
break
|
||||
}
|
||||
// Add a batch of precompile balances to avoid them getting deleted
|
||||
for i := int64(0); i < 256; i++ {
|
||||
genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)}
|
||||
fmt.Println()
|
||||
fmt.Println("Should the precompile-addresses (0x1 .. 0xff) be pre-funded with 1 wei? (advisable yes)")
|
||||
if w.readDefaultYesNo(true) {
|
||||
// Add a batch of precompile balances to avoid them getting deleted
|
||||
for i := int64(0); i < 256; i++ {
|
||||
genesis.Alloc[common.BigToAddress(big.NewInt(i))] = core.GenesisAccount{Balance: big.NewInt(1)}
|
||||
}
|
||||
}
|
||||
// Query the user for some custom extras
|
||||
fmt.Println()
|
||||
@@ -130,53 +139,138 @@ func (w *wizard) makeGenesis() {
|
||||
w.conf.flush()
|
||||
}
|
||||
|
||||
// importGenesis imports a Geth genesis spec into puppeth.
|
||||
func (w *wizard) importGenesis() {
|
||||
// Request the genesis JSON spec URL from the user
|
||||
fmt.Println()
|
||||
fmt.Println("Where's the genesis file? (local file or http/https url)")
|
||||
url := w.readURL()
|
||||
|
||||
// Convert the various allowed URLs to a reader stream
|
||||
var reader io.Reader
|
||||
|
||||
switch url.Scheme {
|
||||
case "http", "https":
|
||||
// Remote web URL, retrieve it via an HTTP client
|
||||
res, err := http.Get(url.String())
|
||||
if err != nil {
|
||||
log.Error("Failed to retrieve remote genesis", "err", err)
|
||||
return
|
||||
}
|
||||
defer res.Body.Close()
|
||||
reader = res.Body
|
||||
|
||||
case "":
|
||||
// Schemaless URL, interpret as a local file
|
||||
file, err := os.Open(url.String())
|
||||
if err != nil {
|
||||
log.Error("Failed to open local genesis", "err", err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
reader = file
|
||||
|
||||
default:
|
||||
log.Error("Unsupported genesis URL scheme", "scheme", url.Scheme)
|
||||
return
|
||||
}
|
||||
// Parse the genesis file and inject it successful
|
||||
var genesis core.Genesis
|
||||
if err := json.NewDecoder(reader).Decode(&genesis); err != nil {
|
||||
log.Error("Invalid genesis spec: %v", err)
|
||||
return
|
||||
}
|
||||
log.Info("Imported genesis block")
|
||||
|
||||
w.conf.Genesis = &genesis
|
||||
w.conf.flush()
|
||||
}
|
||||
|
||||
// manageGenesis permits the modification of chain configuration parameters in
|
||||
// a genesis config and the export of the entire genesis spec.
|
||||
func (w *wizard) manageGenesis() {
|
||||
// Figure out whether to modify or export the genesis
|
||||
fmt.Println()
|
||||
fmt.Println(" 1. Modify existing fork rules")
|
||||
fmt.Println(" 2. Export genesis configuration")
|
||||
fmt.Println(" 2. Export genesis configurations")
|
||||
fmt.Println(" 3. Remove genesis configuration")
|
||||
|
||||
choice := w.read()
|
||||
switch {
|
||||
case choice == "1":
|
||||
switch choice {
|
||||
case "1":
|
||||
// Fork rule updating requested, iterate over each fork
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should Homestead come into effect? (default = %v)\n", w.conf.Genesis.Config.HomesteadBlock)
|
||||
w.conf.Genesis.Config.HomesteadBlock = w.readDefaultBigInt(w.conf.Genesis.Config.HomesteadBlock)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should EIP150 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block)
|
||||
fmt.Printf("Which block should EIP150 (Tangerine Whistle) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP150Block)
|
||||
w.conf.Genesis.Config.EIP150Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP150Block)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should EIP155 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block)
|
||||
fmt.Printf("Which block should EIP155 (Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP155Block)
|
||||
w.conf.Genesis.Config.EIP155Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP155Block)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should EIP158 come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block)
|
||||
fmt.Printf("Which block should EIP158/161 (also Spurious Dragon) come into effect? (default = %v)\n", w.conf.Genesis.Config.EIP158Block)
|
||||
w.conf.Genesis.Config.EIP158Block = w.readDefaultBigInt(w.conf.Genesis.Config.EIP158Block)
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("Which block should Byzantium come into effect? (default = %v)\n", w.conf.Genesis.Config.ByzantiumBlock)
|
||||
w.conf.Genesis.Config.ByzantiumBlock = w.readDefaultBigInt(w.conf.Genesis.Config.ByzantiumBlock)
|
||||
|
||||
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)
|
||||
|
||||
case choice == "2":
|
||||
w.conf.flush()
|
||||
|
||||
case "2":
|
||||
// Save whatever genesis configuration we currently have
|
||||
fmt.Println()
|
||||
fmt.Printf("Which file to save the genesis into? (default = %s.json)\n", w.network)
|
||||
out, _ := json.MarshalIndent(w.conf.Genesis, "", " ")
|
||||
if err := ioutil.WriteFile(w.readDefaultString(fmt.Sprintf("%s.json", w.network)), out, 0644); err != nil {
|
||||
log.Error("Failed to save genesis file", "err", err)
|
||||
}
|
||||
log.Info("Exported existing genesis block")
|
||||
fmt.Printf("Which folder to save the genesis specs into? (default = current)\n")
|
||||
fmt.Printf(" Will create %s.json, %s-aleth.json, %s-harmony.json, %s-parity.json\n", w.network, w.network, w.network, w.network)
|
||||
|
||||
case choice == "3":
|
||||
folder := w.readDefaultString(".")
|
||||
if err := os.MkdirAll(folder, 0755); err != nil {
|
||||
log.Error("Failed to create spec folder", "folder", folder, "err", err)
|
||||
return
|
||||
}
|
||||
out, _ := json.MarshalIndent(w.conf.Genesis, "", " ")
|
||||
|
||||
// Export the native genesis spec used by puppeth and Geth
|
||||
gethJson := filepath.Join(folder, fmt.Sprintf("%s.json", w.network))
|
||||
if err := ioutil.WriteFile((gethJson), out, 0644); err != nil {
|
||||
log.Error("Failed to save genesis file", "err", err)
|
||||
return
|
||||
}
|
||||
log.Info("Saved native genesis chain spec", "path", gethJson)
|
||||
|
||||
// Export the genesis spec used by Aleth (formerly C++ Ethereum)
|
||||
if spec, err := newAlethGenesisSpec(w.network, w.conf.Genesis); err != nil {
|
||||
log.Error("Failed to create Aleth chain spec", "err", err)
|
||||
} else {
|
||||
saveGenesis(folder, w.network, "aleth", spec)
|
||||
}
|
||||
// Export the genesis spec used by Parity
|
||||
if spec, err := newParityChainSpec(w.network, w.conf.Genesis, []string{}); err != nil {
|
||||
log.Error("Failed to create Parity chain spec", "err", err)
|
||||
} else {
|
||||
saveGenesis(folder, w.network, "parity", spec)
|
||||
}
|
||||
// Export the genesis spec used by Harmony (formerly EthereumJ
|
||||
saveGenesis(folder, w.network, "harmony", w.conf.Genesis)
|
||||
|
||||
case "3":
|
||||
// Make sure we don't have any services running
|
||||
if len(w.conf.servers()) > 0 {
|
||||
log.Error("Genesis reset requires all services and servers torn down")
|
||||
@@ -186,8 +280,20 @@ func (w *wizard) manageGenesis() {
|
||||
|
||||
w.conf.Genesis = nil
|
||||
w.conf.flush()
|
||||
|
||||
default:
|
||||
log.Error("That's not something I can do")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// saveGenesis JSON encodes an arbitrary genesis spec into a pre-defined file.
|
||||
func saveGenesis(folder, network, client string, spec interface{}) {
|
||||
path := filepath.Join(folder, fmt.Sprintf("%s-%s.json", network, client))
|
||||
|
||||
out, _ := json.Marshal(spec)
|
||||
if err := ioutil.WriteFile(path, out, 0644); err != nil {
|
||||
log.Error("Failed to save genesis file", "client", client, "err", err)
|
||||
return
|
||||
}
|
||||
log.Info("Saved genesis chain spec", "client", client, "path", path)
|
||||
}
|
||||
|
@@ -61,14 +61,14 @@ func (w *wizard) run() {
|
||||
// Make sure we have a good network name to work with fmt.Println()
|
||||
// Docker accepts hyphens in image names, but doesn't like it for container names
|
||||
if w.network == "" {
|
||||
fmt.Println("Please specify a network name to administer (no spaces or hyphens, please)")
|
||||
fmt.Println("Please specify a network name to administer (no spaces, hyphens or capital letters please)")
|
||||
for {
|
||||
w.network = w.readString()
|
||||
if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") {
|
||||
if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") && strings.ToLower(w.network) == w.network {
|
||||
fmt.Printf("\nSweet, you can set this via --network=%s next time!\n\n", w.network)
|
||||
break
|
||||
}
|
||||
log.Error("I also like to live dangerously, still no spaces or hyphens")
|
||||
log.Error("I also like to live dangerously, still no spaces, hyphens or capital letters")
|
||||
}
|
||||
}
|
||||
log.Info("Administering Ethereum network", "name", w.network)
|
||||
@@ -131,7 +131,20 @@ func (w *wizard) run() {
|
||||
|
||||
case choice == "2":
|
||||
if w.conf.Genesis == nil {
|
||||
w.makeGenesis()
|
||||
fmt.Println()
|
||||
fmt.Println("What would you like to do? (default = create)")
|
||||
fmt.Println(" 1. Create new genesis from scratch")
|
||||
fmt.Println(" 2. Import already existing genesis")
|
||||
|
||||
choice := w.read()
|
||||
switch {
|
||||
case choice == "" || choice == "1":
|
||||
w.makeGenesis()
|
||||
case choice == "2":
|
||||
w.importGenesis()
|
||||
default:
|
||||
log.Error("That's not something I can do")
|
||||
}
|
||||
} else {
|
||||
w.manageGenesis()
|
||||
}
|
||||
@@ -149,7 +162,6 @@ func (w *wizard) run() {
|
||||
} else {
|
||||
w.manageComponents()
|
||||
}
|
||||
|
||||
default:
|
||||
log.Error("That's not something I can do")
|
||||
}
|
||||
|
@@ -41,12 +41,12 @@ func (w *wizard) ensureVirtualHost(client *sshClient, port int, def string) (str
|
||||
// Reverse proxy is not running, offer to deploy a new one
|
||||
fmt.Println()
|
||||
fmt.Println("Allow sharing the port with other services (y/n)? (default = yes)")
|
||||
if w.readDefaultString("y") == "y" {
|
||||
if w.readDefaultYesNo(true) {
|
||||
nocache := false
|
||||
if proxy != nil {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the reverse-proxy be rebuilt from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployNginx(client, w.network, port, nocache); err != nil {
|
||||
log.Error("Failed to deploy reverse-proxy", "err", err)
|
||||
|
@@ -126,7 +126,7 @@ func (w *wizard) deployNode(boot bool) {
|
||||
} else {
|
||||
fmt.Println()
|
||||
fmt.Printf("Reuse previous (%s) signing account (y/n)? (default = yes)\n", key.Address.Hex())
|
||||
if w.readDefaultString("y") != "y" {
|
||||
if !w.readDefaultYesNo(true) {
|
||||
infos.keyJSON, infos.keyPass = "", ""
|
||||
}
|
||||
}
|
||||
@@ -165,7 +165,7 @@ func (w *wizard) deployNode(boot bool) {
|
||||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy Ethereum node container", "err", err)
|
||||
|
@@ -96,7 +96,7 @@ func (w *wizard) deployWallet() {
|
||||
if existed {
|
||||
fmt.Println()
|
||||
fmt.Printf("Should the wallet be built from scratch (y/n)? (default = no)\n")
|
||||
nocache = w.readDefaultString("n") != "n"
|
||||
nocache = w.readDefaultYesNo(false)
|
||||
}
|
||||
if out, err := deployWallet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
|
||||
log.Error("Failed to deploy wallet container", "err", err)
|
||||
|
@@ -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", "",
|
||||
|
@@ -6,7 +6,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
@@ -16,8 +15,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/swarm/storage/feed"
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
"github.com/pborman/uuid"
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
@@ -26,16 +26,28 @@ const (
|
||||
feedRandomDataLength = 8
|
||||
)
|
||||
|
||||
// TODO: retrieve with manifest + extract repeating code
|
||||
func cliFeedUploadAndSync(c *cli.Context) error {
|
||||
func feedUploadAndSyncCmd(ctx *cli.Context, tuid string) error {
|
||||
errc := make(chan error)
|
||||
|
||||
log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true)))))
|
||||
go func() {
|
||||
errc <- feedUploadAndSync(ctx, tuid)
|
||||
}()
|
||||
|
||||
defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size (kb)", filesize) }(time.Now())
|
||||
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)
|
||||
|
||||
generateEndpoints(scheme, cluster, from, to)
|
||||
return fmt.Errorf("timeout after %v sec", timeout)
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
@@ -89,7 +101,7 @@ func cliFeedUploadAndSync(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()
|
||||
@@ -104,7 +116,7 @@ func cliFeedUploadAndSync(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()
|
||||
@@ -119,7 +131,7 @@ func cliFeedUploadAndSync(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()
|
||||
@@ -145,7 +157,7 @@ func cliFeedUploadAndSync(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()
|
||||
@@ -156,7 +168,7 @@ func cliFeedUploadAndSync(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()
|
||||
@@ -167,7 +179,7 @@ func cliFeedUploadAndSync(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()
|
||||
@@ -181,14 +193,14 @@ func cliFeedUploadAndSync(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
|
||||
}
|
||||
@@ -196,20 +208,18 @@ func cliFeedUploadAndSync(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
|
||||
log.Info("uploading to " + endpoints[0] + " and syncing")
|
||||
log.Info("feed uploading to "+httpEndpoint(hosts[0])+" and syncing", "seed", seed)
|
||||
|
||||
f, cleanup := generateRandomFile(filesize * 1000)
|
||||
defer cleanup()
|
||||
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||
|
||||
hash, err := upload(f, endpoints[0])
|
||||
hash, err := upload(randomBytes, httpEndpoint(hosts[0]))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -218,15 +228,12 @@ func cliFeedUploadAndSync(c *cli.Context) error {
|
||||
return err
|
||||
}
|
||||
multihashHex := hexutil.Encode(hashBytes)
|
||||
fileHash, err := digest(f)
|
||||
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 {
|
||||
@@ -236,7 +243,7 @@ func cliFeedUploadAndSync(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 {
|
||||
@@ -246,7 +253,7 @@ func cliFeedUploadAndSync(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 {
|
||||
@@ -257,7 +264,7 @@ func cliFeedUploadAndSync(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} {
|
||||
@@ -265,7 +272,7 @@ func cliFeedUploadAndSync(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
|
||||
}
|
||||
@@ -273,7 +280,7 @@ func cliFeedUploadAndSync(c *cli.Context) error {
|
||||
wg.Done()
|
||||
return
|
||||
}
|
||||
}(url, endpoint, ruid)
|
||||
}(url, httpEndpoint(host), ruid)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -282,37 +289,3 @@ func cliFeedUploadAndSync(c *cli.Context) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func fetchFeed(topic string, user string, endpoint string, original []byte, ruid string) error {
|
||||
log.Trace("sleeping", "ruid", ruid)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
log.Trace("http get request (feed)", "ruid", ruid, "api", endpoint, "topic", topic, "user", user)
|
||||
res, err := http.Get(endpoint + "/bzz-feed:/?topic=" + topic + "&user=" + user)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
@@ -17,23 +17,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
gethmetrics "github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/metrics/influxdb"
|
||||
swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
|
||||
"github.com/ethereum/go-ethereum/swarm/tracing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
endpoints []string
|
||||
includeLocalhost bool
|
||||
cluster string
|
||||
scheme string
|
||||
filesize int
|
||||
from int
|
||||
to int
|
||||
verbosity int
|
||||
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
|
||||
)
|
||||
|
||||
var (
|
||||
allhosts string
|
||||
hosts []string
|
||||
filesize int
|
||||
syncDelay int
|
||||
httpPort int
|
||||
wsPort int
|
||||
verbosity int
|
||||
timeout int
|
||||
single bool
|
||||
trackTimeout int
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -44,33 +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,
|
||||
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",
|
||||
@@ -78,34 +80,111 @@ func main() {
|
||||
Usage: "file size for generated random file in KB",
|
||||
Destination: &filesize,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "sync-delay",
|
||||
Value: 5,
|
||||
Usage: "duration of delay in seconds to wait for content to be synced",
|
||||
Destination: &syncDelay,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "verbosity",
|
||||
Value: 1,
|
||||
Usage: "verbosity",
|
||||
Destination: &verbosity,
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "timeout",
|
||||
Value: 120,
|
||||
Usage: "timeout in seconds after which kill the process",
|
||||
Destination: &timeout,
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "single",
|
||||
Usage: "whether to fetch content from a single node or from all nodes",
|
||||
Destination: &single,
|
||||
},
|
||||
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{
|
||||
utils.MetricsEnabledFlag,
|
||||
swarmmetrics.MetricsInfluxDBEndpointFlag,
|
||||
swarmmetrics.MetricsInfluxDBDatabaseFlag,
|
||||
swarmmetrics.MetricsInfluxDBUsernameFlag,
|
||||
swarmmetrics.MetricsInfluxDBPasswordFlag,
|
||||
swarmmetrics.MetricsInfluxDBTagsFlag,
|
||||
}...)
|
||||
|
||||
app.Flags = append(app.Flags, tracing.Flags...)
|
||||
|
||||
app.Commands = []cli.Command{
|
||||
{
|
||||
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),
|
||||
},
|
||||
}
|
||||
|
||||
sort.Sort(cli.FlagsByName(app.Flags))
|
||||
sort.Sort(cli.CommandsByName(app.Commands))
|
||||
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
tracing.Setup(ctx)
|
||||
return nil
|
||||
}
|
||||
|
||||
app.After = func(ctx *cli.Context) error {
|
||||
return emitMetrics(ctx)
|
||||
}
|
||||
|
||||
err := app.Run(os.Args)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func emitMetrics(ctx *cli.Context) error {
|
||||
if gethmetrics.Enabled {
|
||||
var (
|
||||
endpoint = ctx.GlobalString(swarmmetrics.MetricsInfluxDBEndpointFlag.Name)
|
||||
database = ctx.GlobalString(swarmmetrics.MetricsInfluxDBDatabaseFlag.Name)
|
||||
username = ctx.GlobalString(swarmmetrics.MetricsInfluxDBUsernameFlag.Name)
|
||||
password = ctx.GlobalString(swarmmetrics.MetricsInfluxDBPasswordFlag.Name)
|
||||
tags = ctx.GlobalString(swarmmetrics.MetricsInfluxDBTagsFlag.Name)
|
||||
)
|
||||
|
||||
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
|
||||
}
|
@@ -18,189 +18,177 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
crand "crypto/rand"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"math/rand"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
"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/storage"
|
||||
"github.com/ethereum/go-ethereum/swarm/testutil"
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
cli "gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
func generateEndpoints(scheme string, cluster 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://swarm-%v-%s.stg.swarm-gateways.net", scheme, port, cluster))
|
||||
}
|
||||
}
|
||||
func uploadAndSyncCmd(ctx *cli.Context, tuid string) error {
|
||||
randomBytes := testutil.RandomBytes(seed, filesize*1000)
|
||||
|
||||
if includeLocalhost {
|
||||
endpoints = append(endpoints, "http://localhost:8500")
|
||||
errc := make(chan error)
|
||||
|
||||
go func() {
|
||||
errc <- uplaodAndSync(ctx, randomBytes, 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)
|
||||
|
||||
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 cliUploadAndSync(c *cli.Context) error {
|
||||
log.PrintOrigins(true)
|
||||
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(colorable.NewColorableStderr(), log.TerminalFormat(true))))
|
||||
func trackChunks(testData []byte) error {
|
||||
log.Warn("Test timed out; running chunk debug sequence")
|
||||
|
||||
defer func(now time.Time) { log.Info("total time", "time", time.Since(now), "size (kb)", filesize) }(time.Now())
|
||||
addrs, err := getAllRefs(testData)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Trace("All references retrieved")
|
||||
|
||||
generateEndpoints(scheme, cluster, from, to)
|
||||
// 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
|
||||
}
|
||||
|
||||
log.Info("uploading to " + endpoints[0] + " and syncing")
|
||||
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()
|
||||
|
||||
f, cleanup := generateRandomFile(filesize * 1000)
|
||||
defer cleanup()
|
||||
reader := bytes.NewReader(testData)
|
||||
return fileStore.GetAllReferences(ctx, reader, false)
|
||||
}
|
||||
|
||||
hash, err := upload(f, endpoints[0])
|
||||
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, httpEndpoint(hosts[0]))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
t2 := time.Since(t1)
|
||||
metrics.GetOrRegisterResettingTimer("upload-and-sync.upload-time", nil).Update(t2)
|
||||
|
||||
fhash, err := digest(bytes.NewReader(randomBytes))
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
|
||||
fhash, err := digest(f)
|
||||
if err != nil {
|
||||
log.Error(err.Error())
|
||||
return err
|
||||
}
|
||||
log.Info("uploaded successfully", "tuid", tuid, "hash", hash, "took", t2, "digest", fmt.Sprintf("%x", fhash))
|
||||
|
||||
log.Info("uploaded successfully", "hash", hash, "digest", fmt.Sprintf("%x", fhash))
|
||||
|
||||
time.Sleep(3 * time.Second)
|
||||
time.Sleep(time.Duration(syncDelay) * time.Second)
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
for _, endpoint := range endpoints {
|
||||
if single {
|
||||
randIndex := 1 + rand.Intn(len(hosts)-1)
|
||||
ruid := uuid.New()[:8]
|
||||
wg.Add(1)
|
||||
go func(endpoint string, ruid string) {
|
||||
for {
|
||||
err := fetch(hash, endpoint, fhash, ruid)
|
||||
start := time.Now()
|
||||
err := fetch(hash, endpoint, fhash, ruid, tuid)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ended := time.Since(start)
|
||||
|
||||
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
|
||||
}
|
||||
}(endpoint, ruid)
|
||||
}(httpEndpoint(hosts[randIndex]), ruid)
|
||||
} else {
|
||||
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, tuid)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
ended := time.Since(start)
|
||||
|
||||
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
|
||||
}
|
||||
}(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 {
|
||||
log.Trace("sleeping", "ruid", ruid)
|
||||
time.Sleep(3 * time.Second)
|
||||
|
||||
log.Trace("http get request", "ruid", ruid, "api", endpoint, "hash", hash)
|
||||
client := &http.Client{Transport: &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}}
|
||||
res, err := client.Get(endpoint + "/bzz:/" + hash + "/")
|
||||
if err != nil {
|
||||
log.Warn(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(f *os.File, endpoint string) (string, error) {
|
||||
var out bytes.Buffer
|
||||
cmd := exec.Command("swarm", "--bzzapi", endpoint, "up", f.Name())
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
hash := strings.TrimRight(out.String(), "\r\n")
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// generateRandomFile is creating a temporary file with the requested byte size
|
||||
func generateRandomFile(size int) (f *os.File, teardown func()) {
|
||||
// create a tmp file
|
||||
tmp, err := ioutil.TempFile("", "swarm-test")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// callback for tmp file cleanup
|
||||
teardown = func() {
|
||||
tmp.Close()
|
||||
os.Remove(tmp.Name())
|
||||
}
|
||||
|
||||
buf := make([]byte, size)
|
||||
_, err = crand.Read(buf)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ioutil.WriteFile(tmp.Name(), buf, 0755)
|
||||
|
||||
return tmp, teardown
|
||||
}
|
||||
|
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,14 @@ 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",
|
||||
}
|
||||
DeveloperFlag = cli.BoolFlag{
|
||||
Name: "dev",
|
||||
Usage: "Ephemeral proof-of-authority network with a pre-funded developer account, mining enabled",
|
||||
@@ -182,6 +190,10 @@ var (
|
||||
Name: "lightkdf",
|
||||
Usage: "Reduce key-derivation RAM & CPU usage at some expense of KDF strength",
|
||||
}
|
||||
WhitelistFlag = cli.StringFlag{
|
||||
Name: "whitelist",
|
||||
Usage: "Comma separated block number-to-hash mappings to enforce (<number>=<hash>)",
|
||||
}
|
||||
// Dashboard settings
|
||||
DashboardEnabledFlag = cli.BoolFlag{
|
||||
Name: metrics.DashboardEnabledFlag,
|
||||
@@ -399,6 +411,10 @@ var (
|
||||
Name: "vmdebug",
|
||||
Usage: "Record information useful for VM and contract debugging",
|
||||
}
|
||||
RPCGlobalGasCap = cli.Uint64Flag{
|
||||
Name: "rpc.gascap",
|
||||
Usage: "Sets a cap on gas that can be used in eth_call/estimateGas",
|
||||
}
|
||||
// Logging and debug settings
|
||||
EthStatsURLFlag = cli.StringFlag{
|
||||
Name: "ethstats",
|
||||
@@ -606,14 +622,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{
|
||||
@@ -639,6 +655,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)")
|
||||
@@ -693,6 +712,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.
|
||||
}
|
||||
@@ -720,6 +741,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.
|
||||
}
|
||||
@@ -824,19 +847,15 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
|
||||
// makeDatabaseHandles raises out the number of allowed file handles per process
|
||||
// for Geth and returns half of the allowance to assign to the database.
|
||||
func makeDatabaseHandles() int {
|
||||
limit, err := fdlimit.Current()
|
||||
limit, err := fdlimit.Maximum()
|
||||
if err != nil {
|
||||
Fatalf("Failed to retrieve file descriptor allowance: %v", err)
|
||||
}
|
||||
if limit < 2048 {
|
||||
if err := fdlimit.Raise(2048); err != nil {
|
||||
Fatalf("Failed to raise file descriptor allowance: %v", err)
|
||||
}
|
||||
raised, err := fdlimit.Raise(uint64(limit))
|
||||
if err != nil {
|
||||
Fatalf("Failed to raise file descriptor allowance: %v", err)
|
||||
}
|
||||
if limit > 2048 { // cap database file descriptors even if more is available
|
||||
limit = 2048
|
||||
}
|
||||
return limit / 2 // Leave half for networking and other stuff
|
||||
return int(raised / 2) // Leave half for networking and other stuff
|
||||
}
|
||||
|
||||
// MakeAddress converts an account specified directly as a hex encoded string or
|
||||
@@ -977,7 +996,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) {
|
||||
@@ -1001,6 +1019,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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1077,6 +1097,29 @@ func setEthash(ctx *cli.Context, cfg *eth.Config) {
|
||||
}
|
||||
}
|
||||
|
||||
func setWhitelist(ctx *cli.Context, cfg *eth.Config) {
|
||||
whitelist := ctx.GlobalString(WhitelistFlag.Name)
|
||||
if whitelist == "" {
|
||||
return
|
||||
}
|
||||
cfg.Whitelist = make(map[uint64]common.Hash)
|
||||
for _, entry := range strings.Split(whitelist, ",") {
|
||||
parts := strings.Split(entry, "=")
|
||||
if len(parts) != 2 {
|
||||
Fatalf("Invalid whitelist entry: %s", entry)
|
||||
}
|
||||
number, err := strconv.ParseUint(parts[0], 0, 64)
|
||||
if err != nil {
|
||||
Fatalf("Invalid whitelist block number %s: %v", parts[0], err)
|
||||
}
|
||||
var hash common.Hash
|
||||
if err = hash.UnmarshalText([]byte(parts[1])); err != nil {
|
||||
Fatalf("Invalid whitelist hash %s: %v", parts[1], err)
|
||||
}
|
||||
cfg.Whitelist[number] = hash
|
||||
}
|
||||
}
|
||||
|
||||
// checkExclusive verifies that only a single instance of the provided flags was
|
||||
// set by the user. Each flag might optionally be followed by a string type to
|
||||
// specialize it further.
|
||||
@@ -1134,7 +1177,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)
|
||||
@@ -1142,6 +1185,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||
setGPO(ctx, &cfg.GPO)
|
||||
setTxPool(ctx, &cfg.TxPool)
|
||||
setEthash(ctx, cfg)
|
||||
setWhitelist(ctx, cfg)
|
||||
|
||||
if ctx.GlobalIsSet(SyncModeFlag.Name) {
|
||||
cfg.SyncMode = *GlobalTextMarshaler(ctx, SyncModeFlag.Name).(*downloader.SyncMode)
|
||||
@@ -1155,7 +1199,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||
if ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
|
||||
}
|
||||
|
||||
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
|
||||
cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
|
||||
}
|
||||
@@ -1217,6 +1260,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||
if ctx.GlobalIsSet(EVMInterpreterFlag.Name) {
|
||||
cfg.EVMInterpreter = ctx.GlobalString(EVMInterpreterFlag.Name)
|
||||
}
|
||||
if ctx.GlobalIsSet(RPCGlobalGasCap.Name) {
|
||||
cfg.RPCGasCap = new(big.Int).SetUint64(ctx.GlobalUint64(RPCGlobalGasCap.Name))
|
||||
}
|
||||
|
||||
// Override any default configs for hard coded networks.
|
||||
switch {
|
||||
@@ -1230,6 +1276,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
|
||||
@@ -1334,18 +1385,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 (
|
||||
@@ -1370,6 +1438,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")
|
||||
}
|
||||
@@ -1380,7 +1450,6 @@ func MakeGenesis(ctx *cli.Context) *core.Genesis {
|
||||
func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chainDb ethdb.Database) {
|
||||
var err error
|
||||
chainDb = MakeChainDatabase(ctx, stack)
|
||||
|
||||
config, _, err := core.SetupGenesisBlock(chainDb, MakeGenesis(ctx))
|
||||
if err != nil {
|
||||
Fatalf("%v", err)
|
||||
|
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
71
common/fdlimit/fdlimit_darwin.go
Normal file
71
common/fdlimit/fdlimit_darwin.go
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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 fdlimit
|
||||
|
||||
import "syscall"
|
||||
|
||||
// hardlimit is the number of file descriptors allowed at max by the kernel.
|
||||
const hardlimit = 10240
|
||||
|
||||
// Raise tries to maximize the file descriptor allowance of this process
|
||||
// to the maximum hard-limit allowed by the OS.
|
||||
// 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 0, err
|
||||
}
|
||||
// Try to update the limit to the max allowance
|
||||
limit.Cur = limit.Max
|
||||
if limit.Cur > max {
|
||||
limit.Cur = max
|
||||
}
|
||||
if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// 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
|
||||
// process.
|
||||
func Current() (int, error) {
|
||||
var limit syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(limit.Cur), nil
|
||||
}
|
||||
|
||||
// Maximum retrieves the maximum number of file descriptors this process is
|
||||
// allowed to request for itself.
|
||||
func Maximum() (int, error) {
|
||||
// Retrieve the maximum allowed by dynamic OS limits
|
||||
var limit syscall.Rlimit
|
||||
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// Cap it to OPEN_MAX (10240) because macos is a special snowflake
|
||||
if limit.Max > hardlimit {
|
||||
limit.Max = hardlimit
|
||||
}
|
||||
return int(limit.Max), nil
|
||||
}
|
@@ -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 {
|
||||
|
@@ -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/>.
|
||||
|
||||
// +build linux darwin netbsd openbsd solaris
|
||||
// +build linux netbsd openbsd solaris
|
||||
|
||||
package fdlimit
|
||||
|
||||
@@ -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,31 @@
|
||||
|
||||
package fdlimit
|
||||
|
||||
import "errors"
|
||||
import "fmt"
|
||||
|
||||
// hardlimit is the number of file descriptors allowed at max by the kernel.
|
||||
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,
|
||||
@@ -279,7 +279,7 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header,
|
||||
number := header.Number.Uint64()
|
||||
|
||||
// Don't waste time checking blocks from the future
|
||||
if header.Time.Cmp(big.NewInt(time.Now().Unix())) > 0 {
|
||||
if header.Time > uint64(time.Now().Unix()) {
|
||||
return consensus.ErrFutureBlock
|
||||
}
|
||||
// Checkpoint blocks need to enforce zero beneficiary
|
||||
@@ -351,7 +351,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
|
||||
if parent == nil || parent.Number.Uint64() != number-1 || parent.Hash() != header.ParentHash {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
if parent.Time.Uint64()+c.config.Period > header.Time.Uint64() {
|
||||
if parent.Time+c.config.Period > header.Time {
|
||||
return ErrInvalidTimestamp
|
||||
}
|
||||
// Retrieve the snapshot needed to verify this header and cache it
|
||||
@@ -570,9 +570,9 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
|
||||
if parent == nil {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
header.Time = new(big.Int).Add(parent.Time, new(big.Int).SetUint64(c.config.Period))
|
||||
if header.Time.Int64() < time.Now().Unix() {
|
||||
header.Time = big.NewInt(time.Now().Unix())
|
||||
header.Time = parent.Time + c.config.Period
|
||||
if header.Time < uint64(time.Now().Unix()) {
|
||||
header.Time = uint64(time.Now().Unix())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -637,7 +637,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c
|
||||
}
|
||||
}
|
||||
// Sweet, the protocol permits us to sign the block, wait for our time
|
||||
delay := time.Unix(header.Time.Int64(), 0).Sub(time.Now()) // nolint: gosimple
|
||||
delay := time.Unix(int64(header.Time), 0).Sub(time.Now()) // nolint: gosimple
|
||||
if header.Difficulty.Cmp(diffNoTurn) == 0 {
|
||||
// It's not our turn explicitly to sign, delay it a bit
|
||||
wiggle := time.Duration(len(snap.Signers)/2+1) * wiggleTime
|
||||
|
@@ -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)
|
||||
|
@@ -716,7 +716,7 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) {
|
||||
Difficulty: big.NewInt(167925187834220),
|
||||
GasLimit: 4015682,
|
||||
GasUsed: 0,
|
||||
Time: big.NewInt(1488928920),
|
||||
Time: 1488928920,
|
||||
Extra: []byte("www.bw.com"),
|
||||
MixDigest: common.HexToHash("0x3e140b0784516af5e5ec6730f2fb20cca22f32be399b9e4ad77d32541f798cd0"),
|
||||
Nonce: types.EncodeNonce(0xf400cd0006070c49),
|
||||
|
@@ -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.
|
||||
@@ -63,7 +63,6 @@ var (
|
||||
// codebase, inherently breaking if the engine is swapped out. Please put common
|
||||
// error types into the consensus package.
|
||||
var (
|
||||
errLargeBlockTime = errors.New("timestamp too big")
|
||||
errZeroBlockTime = errors.New("timestamp equals parent's")
|
||||
errTooManyUncles = errors.New("too many uncles")
|
||||
errDuplicateUncle = errors.New("duplicate uncle")
|
||||
@@ -242,20 +241,16 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
|
||||
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
|
||||
}
|
||||
// Verify the header's timestamp
|
||||
if uncle {
|
||||
if header.Time.Cmp(math.MaxBig256) > 0 {
|
||||
return errLargeBlockTime
|
||||
}
|
||||
} else {
|
||||
if header.Time.Cmp(big.NewInt(time.Now().Add(allowedFutureBlockTime).Unix())) > 0 {
|
||||
if !uncle {
|
||||
if header.Time > uint64(time.Now().Add(allowedFutureBlockTime).Unix()) {
|
||||
return consensus.ErrFutureBlock
|
||||
}
|
||||
}
|
||||
if header.Time.Cmp(parent.Time) <= 0 {
|
||||
if header.Time <= parent.Time {
|
||||
return errZeroBlockTime
|
||||
}
|
||||
// Verify the block's difficulty based in it's timestamp and parent's difficulty
|
||||
expected := ethash.CalcDifficulty(chain, header.Time.Uint64(), parent)
|
||||
expected := ethash.CalcDifficulty(chain, header.Time, parent)
|
||||
|
||||
if expected.Cmp(header.Difficulty) != 0 {
|
||||
return fmt.Errorf("invalid difficulty: have %v, want %v", header.Difficulty, expected)
|
||||
@@ -349,7 +344,7 @@ func makeDifficultyCalculator(bombDelay *big.Int) func(time uint64, parent *type
|
||||
// ) + 2^(periodCount - 2)
|
||||
|
||||
bigTime := new(big.Int).SetUint64(time)
|
||||
bigParentTime := new(big.Int).Set(parent.Time)
|
||||
bigParentTime := new(big.Int).SetUint64(parent.Time)
|
||||
|
||||
// holds intermediate values to make the algo easier to read & audit
|
||||
x := new(big.Int)
|
||||
@@ -408,7 +403,7 @@ func calcDifficultyHomestead(time uint64, parent *types.Header) *big.Int {
|
||||
// ) + 2^(periodCount - 2)
|
||||
|
||||
bigTime := new(big.Int).SetUint64(time)
|
||||
bigParentTime := new(big.Int).Set(parent.Time)
|
||||
bigParentTime := new(big.Int).SetUint64(parent.Time)
|
||||
|
||||
// holds intermediate values to make the algo easier to read & audit
|
||||
x := new(big.Int)
|
||||
@@ -456,7 +451,7 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int {
|
||||
bigParentTime := new(big.Int)
|
||||
|
||||
bigTime.SetUint64(time)
|
||||
bigParentTime.Set(parent.Time)
|
||||
bigParentTime.SetUint64(parent.Time)
|
||||
|
||||
if bigTime.Sub(bigTime, bigParentTime).Cmp(params.DurationLimit) < 0 {
|
||||
diff.Add(parent.Difficulty, adjust)
|
||||
@@ -558,7 +553,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header)
|
||||
if parent == nil {
|
||||
return consensus.ErrUnknownAncestor
|
||||
}
|
||||
header.Difficulty = ethash.CalcDifficulty(chain, header.Time.Uint64(), parent)
|
||||
header.Difficulty = ethash.CalcDifficulty(chain, header.Time, parent)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -575,7 +570,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,
|
||||
|
@@ -76,7 +76,7 @@ func TestCalcDifficulty(t *testing.T) {
|
||||
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
|
||||
diff := CalcDifficulty(config, test.CurrentTimestamp, &types.Header{
|
||||
Number: number,
|
||||
Time: new(big.Int).SetUint64(test.ParentTimestamp),
|
||||
Time: test.ParentTimestamp,
|
||||
Difficulty: test.ParentDifficulty,
|
||||
})
|
||||
if diff.Cmp(test.CurrentDifficulty) != 0 {
|
||||
|
@@ -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
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user