Compare commits

...

228 Commits

Author SHA1 Message Date
Chris Chinchilla
8b6f5d4f72 Correct Clef typo 2020-02-21 14:56:58 +01:00
gary rong
2e1ecc02bd les, miner, accounts/abi/bind: fix load-sensitive unit tests (#20698) 2020-02-20 13:05:54 +01:00
Guillaume Ballet
6df973df27 go.mod: upgrade goja to latest (#20700)
The new goja version supports the 'escape' and 'unescape' built-in functions.
This fixes #20693
2020-02-20 12:46:47 +01:00
Gregory Markou
4be8840120 core/vm: use dedicated SLOAD gas constant for EIP-2200 (#20646) 2020-02-18 15:07:41 +01:00
Péter Szilágyi
529b81dadb params: begin v1.9.12 release cycle 2020-02-18 13:27:39 +02:00
Péter Szilágyi
6a62fe399b params: release Geth v1.9.11 stable 2020-02-18 13:26:00 +02:00
Felix Lange
dae3aee5ff les: add bootstrap nodes as initial discoveries (#20688) 2020-02-18 13:24:05 +02:00
Péter Szilágyi
05ccbb5edd Merge pull request #20687 from karalabe/cht-1.9.11
params: update CHTs for the v1.9.11 release
2020-02-18 10:57:05 +02:00
Péter Szilágyi
4f55e24c02 params: update CHTs for the v1.9.11 release 2020-02-18 10:55:44 +02:00
Felix Lange
91b228966e rpc: remove startup error for invalid modules, log it instead (#20684)
This removes the error added in #20597 in favor of a log message at
error level. Failing to start broke a bunch of people's setups and is
probably not the right thing to do for this check.
2020-02-17 18:33:32 +02:00
Boqin Qin
1b9c5b393b all: fix goroutine leaks in unit tests by adding 1-elem channel buffer (#20666)
This fixes a bunch of cases where a timeout in the test would leak
a goroutine.
2020-02-17 17:33:11 +01:00
Felix Lange
57d4898e29 p2p/dnsdisc: re-check tree root when leaf resolution fails (#20682)
This adds additional logic to re-resolve the root name of a tree when a
couple of leaf requests have failed. We need this change to avoid
getting into a failure state where leaf requests keep failing for half
an hour when the tree has been updated.
2020-02-17 15:23:25 +01:00
Péter Szilágyi
c2117982b8 Merge pull request #20678 from karalabe/broadcast-sqrt-proper
eth: don't enforce minimum broadcast, fix broadcast test
2020-02-17 14:43:30 +02:00
Felix Lange
1c4c486a85 cmd/ethkey: speed up test by using weaker scrypt parameters (#20680) 2020-02-17 13:22:52 +02:00
Felix Lange
ac72787768 p2p: remove MeteredPeerEvent (#20679)
This event was added for the dashboard, but we don't need it anymore
since the dashboard is gone.
2020-02-17 13:22:14 +02:00
Péter Szilágyi
26284ec3cc Merge pull request #20681 from karalabe/go1.13.8
travis, appveyor, build: bump builder Go to 1.13.8
2020-02-17 13:15:17 +02:00
Péter Szilágyi
fef8c985bc travis, appveyor, build: bump builder Go to 1.13.8 2020-02-17 13:13:24 +02:00
Péter Szilágyi
36a1e0b67d eth: don't enforce minimum broadcast, fix broadcast test 2020-02-17 12:01:03 +02:00
Boqin Qin
37531b1884 cmd/faucet: protect f.reqs with Rlock to prevent data race (#20669)
* cmd/faucet: add Rlock to protect f.reqs in apiHandler

* cmd/faucet: make a locked copy of f.reqs
2020-02-15 20:14:29 +02:00
Martin Holst Swende
855690523a core: ensure state exists for prefetcher (#20627) 2020-02-14 10:54:02 +02:00
Felix Lange
38d1b0cba2 cmd/geth: enable DNS discovery by default (#20660)
* node: expose config in service context

* eth: integrate p2p/dnsdisc

* cmd/geth: add some DNS flags

* eth: remove DNS URLs

* cmd/utils: configure DNS names for testnets

* params: update DNS URLs

* cmd/geth: configure mainnet DNS

* cmd/utils: rename DNS flag and fix flag processing

* cmd/utils: remove debug print

* node: fix test
2020-02-13 15:38:30 +02:00
Péter Szilágyi
eddcecc160 Merge pull request #20234 from rjl493456442/newtxhashes_2
core, eth: announce based transaction propagation
2020-02-13 15:28:34 +02:00
Péter Szilágyi
9938d954c8 eth: rework tx fetcher to use O(1) ops + manage network requests 2020-02-13 15:27:15 +02:00
Felix Lange
90caa2cabb p2p: new dial scheduler (#20592)
* p2p: new dial scheduler

This change replaces the peer-to-peer dial scheduler with a new and
improved implementation. The new code is better than the previous
implementation in two key aspects:

- The time between discovery of a node and dialing that node is
  significantly lower in the new version. The old dialState kept
  a buffer of nodes and launched a task to refill it whenever the buffer
  became empty. This worked well with the discovery interface we used to
  have, but doesn't really work with the new iterator-based discovery
  API.

- Selection of static dial candidates (created by Server.AddPeer or
  through static-nodes.json) performs much better for large amounts of
  static peers. Connections to static nodes are now limited like dynanic
  dials and can no longer overstep MaxPeers or the dial ratio.

* p2p/simulations/adapters: adapt to new NodeDialer interface

* p2p: re-add check for self in checkDial

* p2p: remove peersetCh

* p2p: allow static dials when discovery is disabled

* p2p: add test for dialScheduler.removeStatic

* p2p: remove blank line

* p2p: fix documentation of maxDialPeers

* p2p: change "ok" to "added" in static node log

* p2p: improve dialTask docs

Also increase log level for "Can't resolve node"

* p2p: ensure dial resolver is truly nil without discovery

* p2p: add "looking for peers" log message

* p2p: clean up Server.run comments

* p2p: fix maxDialedConns for maxpeers < dialRatio

Always allocate at least one dial slot unless dialing is disabled using
NoDial or MaxPeers == 0. Most importantly, this fixes MaxPeers == 1 to
dedicate the sole slot to dialing instead of listening.

* p2p: fix RemovePeer to disconnect the peer again

Also make RemovePeer synchronous and add a test.

* p2p: remove "Connection set up" log message

* p2p: clean up connection logging

We previously logged outgoing connection failures up to three times.

- in SetupConn() as "Setting up connection failed addr=..."
- in setupConn() with an error-specific message and "id=... addr=..."
- in dial() as "Dial error task=..."

This commit ensures a single log message is emitted per failure and adds
"id=... addr=... conn=..." everywhere (id= omitted when the ID isn't
known yet).

Also avoid printing a log message when a static dial fails but can't be
resolved because discv4 is disabled. The light client hit this case all
the time, increasing the message count to four lines per failed
connection.

* p2p: document that RemovePeer blocks
2020-02-13 11:10:03 +01:00
Boqin Qin
5f2002bbcc accounts: add walletsNoLock to avoid double read lock (#20655) 2020-02-12 15:20:50 +01:00
Boqin Qin
a9614c3c91 event, p2p/simulations/adapters: fix rare goroutine leaks (#20657)
Co-authored-by: Felix Lange <fjl@twurst.com>
2020-02-12 15:19:47 +01:00
Marius van der Wijden
46c4b699c8 accounts/abi/bind/backends: add support for historical state (#20644) 2020-02-12 11:33:17 +01:00
Boqin Qin
1821328162 event: add missing unlock before panic (#20653) 2020-02-12 10:33:31 +01:00
Adam Schmideg
8045504abf les: log disconnect reason when light server is not synced (#20643)
Co-authored-by: ligi <ligi@ligi.de>
2020-02-11 16:46:32 +01:00
Felix Lange
c22fdec3c7 common/mclock: add NewTimer and Timer.Reset (#20634)
These methods can be helpful when migrating existing timer code.
2020-02-11 16:36:49 +01:00
rjl493456442
049e17116e core, eth: implement eth/65 transaction fetcher 2020-02-11 13:56:36 +02:00
winsvega
dcffb7777f cmd/geth retesteth: add eth_getBlockByHash (#20621) 2020-02-11 10:54:05 +01:00
chabashilah
8694d14e65 signer: add bytes32 as valid primitive (#20609) 2020-02-11 10:52:51 +01:00
Adam Schmideg
172f7778fe rpc: add error when call result parameter is not addressable (#20638) 2020-02-11 09:48:58 +01:00
AmitBRD
34bb132b10 graphql: add transaction signature values (#20623)
The feature update allows the GraphQL API endpoint to retrieve
transaction signature R,S,V parameters.

Co-authored-by: amitshah <amitshah0t7@gmail.com>
Co-authored-by: Felix Lange <fjl@twurst.com>
2020-02-09 21:50:44 +01:00
Nick Ward
675f4e75b8 README.md: update evm usage example (#20635) 2020-02-09 17:18:47 +01:00
Martin Holst Swende
4a231cd951 internal/ethapi: return non-null "number" for pending block (#20616)
Fixes: #20587, ethereum/web3.py#1572
2020-02-07 10:44:32 +01:00
Felix Lange
976a0f5558 cmd/devp2p: fix Route53 TXT record splitting (#20626)
For longer records and subtree entries, the deployer created two
separate TXT records. This doesn't work as intended because the client
will receive the two records in arbitrary order. The fix is to encode
longer values as "string1""string2" instead of "string1", "string2".
This encoding creates a single record on AWS Route53.
2020-02-05 15:29:59 +01:00
Martin Holst Swende
a1313b5b1e trie: make hasher parallel when number of changes are large (#20488)
* trie: make hasher parallel when number of changes are large

* trie: remove unused field dirtyCount

* trie: rename unhashedCount/unhashed
2020-02-04 14:02:38 +02:00
meowsbits
711ed74e09 cmd/geth: add 'dumpgenesis' command (#20191)
Adds the 'geth dumpgenesis' command, which writes the configured
genesis in JSON format to stdout. This provides a way to generate the
data (structure and content) that can then be used with the 'geth init'
command.
2020-02-04 11:49:13 +01:00
Martin Holst Swende
058a4ac5f1 core/evm: less iteration in blockhash (#20589)
* core/vm/runtime: add test for blockhash

* core/evm: less iteration in blockhash

* core/vm/runtime: nitpickfix

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2020-02-04 12:32:31 +02:00
tintin
33791dbeb5 tracers: avoid panic on invalid arguments (#20612)
* add regression tests for #20611

* eth/tracers: fix panics occurring for invalid params in js-tracers

Co-authored-by: Martin Holst Swende <martin@swende.se>
2020-02-04 09:55:07 +01:00
Martin Holst Swende
5a9c96454e trie: separate hashes and committer, collapse on commit
* trie:  make db insert use size instead of full data

* core/state: minor optimization in state onleaf allocation

* trie: implement dedicated committer and hasher

* trie: use dedicated committer/hasher

* trie: linter nitpicks

* core/state, trie: avoid unnecessary storage trie load+commit

* trie: review feedback, mainly docs + minor changes

* trie: start deprecating old hasher

* trie: fix misspell+lint

* trie: deprecate hasher.go, make proof framework use new hasher

* trie: rename pure_committer/hasher to committer/hasher

* trie, core/state: fix review concerns

* trie: more review concerns

* trie: make commit collapse into hashnode, don't touch dirtyness

* trie: goimports fixes

* trie: remove panics
2020-02-03 17:28:30 +02:00
Felix Lange
4cc89a5a32 internal/build: don't crash in DownloadFile when offline (#20595) 2020-02-03 17:22:46 +02:00
Martin Holst Swende
15d09038a6 params: update bootnodes (#20610) 2020-01-31 14:12:19 +01:00
Martin Holst Swende
3c776c7199 retesteth: clean txpool on rewind, default dao support (#20596) 2020-01-31 12:00:37 +01:00
Guillaume Ballet
24cab2d535 core/vm/runtime: fix typos in comment (#20608) 2020-01-30 11:21:10 +01:00
Guillaume Ballet
594e038e75 signer/rules: use goja and remove otto (#20599)
* signer: replace otto with goja

* go.mod: remove Otto
2020-01-29 13:47:56 +01:00
Felix Lange
a903912b96 rpc: check module availability at startup (#20597)
Fixes #20467

Co-authored-by: meowsbits <45600330+meowsbits@users.noreply.github.com>
2020-01-28 10:37:08 +01:00
Zhou Zhiyao
44c365c3e2 rpc: reset writeConn when conn is closed on readErr (#20414)
This change makes the client attempt to reconnect when a write fails.
We already had reconnect support, but the reconnect would previously
happen on the next call after an error. Being more eager leads to a
smoother experience overall.
2020-01-27 14:03:15 +01:00
Guillaume Ballet
7b68975a00 console, internal/jsre: use github.com/dop251/goja (#20470)
This replaces the JavaScript interpreter used by the console with goja,
which is actively maintained and a lot faster than otto. Clef still uses otto
and eth/tracers still uses duktape, so we are currently dependent on three
different JS interpreters. We're looking to replace the remaining uses of otto
soon though.
2020-01-27 11:50:48 +01:00
Guillaume Ballet
60deeb103e cmd/evm: accept --input for disasm command (#20548) 2020-01-27 10:05:21 +01:00
Martin Holst Swende
0b284f6c6c cmd/geth/retesteth: use canon head instead of keeping alternate count (#20572) 2020-01-23 20:55:56 +01:00
Guillaume Ballet
8a5c81349e eth: fix comment typo in handler.go (#20575) 2020-01-23 16:08:06 +01:00
Martin Holst Swende
33c56ebc67 cmd: implement abidump (#19958)
* abidump: implement abi dump command

* cmd/abidump: add license
2020-01-21 15:51:36 +01:00
Felix Lange
31baf3a9af log, internal/debug: delete RotatingFileHandler (#20586)
* log: delete RotatingFileHandler

We added this for the dashboard, which is gone now. The
handler never really worked well and had data race and file
handling issues.

* internal/debug: remove unused RotatingFileHandler setup code
2020-01-21 14:57:33 +02:00
Péter Szilágyi
ad2fc7c6a6 params: begin Geth v1.9.11 release cycle 2020-01-20 12:32:47 +02:00
Péter Szilágyi
58cf5686ea params: release Geth v1.9.10 2020-01-20 12:27:51 +02:00
Péter Szilágyi
b4aa4a6965 Merge pull request #20580 from karalabe/cht-1.9.10
params: update CHTs for v1.9.10 release
2020-01-20 11:18:37 +02:00
gary rong
b88b4632c2 core: fix chain indexer unit test (#20506) 2020-01-20 10:38:08 +02:00
Péter Szilágyi
1f1cefc036 params: update CHTs for v1.9.10 release 2020-01-20 10:28:49 +02:00
Péter Szilágyi
4c8fcd93da Merge pull request #20579 from karalabe/android-go-1.13.6
travis: bump Android builder to Go 1.13.6
2020-01-20 00:55:00 +02:00
Péter Szilágyi
fcc84c38dd travis: bump Android builder to Go 1.13.6 2020-01-20 00:54:20 +02:00
Péter Szilágyi
6d200efe72 Merge pull request #20578 from karalabe/win-go-1.13.6
appveyor: bump Go to 1.13.6 on Windows
2020-01-20 00:53:13 +02:00
Péter Szilágyi
92956e2930 appveyor: bump Go to 1.13.6 on Windows 2020-01-20 00:50:59 +02:00
Martin Holst Swende
9b09c0fc83 * trie: utilize callbacks instead of amassing lists in ref/unref (#20529)
* trie/tests: add benchmarks and update trie tests

* trie: update benchmark tests

* trie: utilize callbacks instead of amassing lists of hashes in database ref/unref

* trie: replace remaining non-callback based accesses
2020-01-17 13:59:45 +02:00
gary rong
770316dc20 core, light: write chain data in atomic way (#20287)
* core: write chain data in atomic way

* core, light: address comments

* core, light: fix linter

* core, light: address comments
2020-01-17 12:49:32 +02:00
Felix Lange
0af96d2556 cmd/devp2p: submit Route53 changes in batches (#20524)
This change works around the 32k RDATA character limit per change
request and fixes several issues in the deployer which prevented it from
working for our production trees.
2020-01-17 11:32:29 +01:00
Felix Lange
d5acc5ed9e p2p: ensure Server.loop is ticking even if discovery hangs (#20573)
This is a temporary fix for a problem which started happening when the
dialer was changed to read nodes from an enode.Iterator. Before the
iterator change, discovery queries would always return within a couple
seconds even if there was no Internet access. Since the iterator won't
return unless a node is actually found, discoverTask can take much
longer. This means that the 'emergency connect' logic might not execute
in time, leading to a stuck node.
2020-01-17 12:29:16 +02:00
Felix Lange
fcafa0baa5 p2p: wait for listener goroutines on shutdown (#20569)
* p2p: wait for goroutine exit, fixes #20558

* p2p: wait for all slots on exit

Co-authored-by: Martin Holst Swende <martin@swende.se>
2020-01-16 14:10:15 +02:00
Guillaume Ballet
1ee754b056 build: upgrade golangci to 1.22.2 (#20566)
* build: upgrade golangci to 1.22.2

* .golangci.yml: don't fail on asset deadcode
2020-01-16 14:09:38 +02:00
Péter Szilágyi
b3b8d36995 Merge pull request #20570 from karalabe/ppa-focal-go-1.13.6
travis, build: enable Ubuntu Focal and Go 1.13.6 on PPA
2020-01-16 14:09:00 +02:00
Péter Szilágyi
9b32f592dc travis, build: enable Ubuntu Focal and Go 1.13.6 on PPA 2020-01-16 13:28:32 +02:00
Felix Lange
3e97b04a3d build: put GOPATH in /tmp on launchpad (#20564)
* build: put GOPATH in /tmp on launchpad

* build: don't remove GOPATH from go tool environment
2020-01-16 13:03:41 +02:00
Martin Holst Swende
f20c8d495a eth: increase timeout to fix a spurious travis test failure (#20560) 2020-01-15 15:30:50 +01:00
Felix Lange
8704e8a8fc build: fix makefile HOME reference (#20562) 2020-01-15 12:38:35 +02:00
Felix Lange
94e8418939 build: attempt to fix debian build failure without GOPATH (#20561) 2020-01-15 11:37:31 +02:00
Felix Lange
feda78e052 build: remove env.sh (#20541)
* build: remove env.sh

This removes the dirty symlink-to-self hack we've had for years. The
script was added to enable building without GOPATH and did that job
reliably for all this time. We can remove the workaround because modern
Go supports building without GOPATH natively.

* Makefile: add GO111MODULE=on to environment
2020-01-14 14:13:14 +02:00
Péter Szilágyi
8592a57553 Merge pull request #20555 from holiman/cripple_txsize
core: set max tx size down to 2 slots (64KB)
2020-01-14 13:18:22 +02:00
Martin Holst Swende
b2de0bd87b core: set max tx size down to 2 slots (64KB) 2020-01-14 11:49:36 +01:00
Péter Szilágyi
e9e69d6e29 Merge pull request #20546 from karalabe/validate-block-broadcast
eth: check propagated block malformation on receiption
2020-01-13 15:19:14 +02:00
Péter Szilágyi
a90cc66f3c eth: check propagated block malformation on receiption 2020-01-13 14:23:54 +02:00
MichaelRiabzev-StarkWare
8bd37a1d91 core: count tx size in slots, bump max size ot 4x32KB (#20352)
* tests for tx size

* alow multiple slots transactions

* tests for tx size limit (32 KB)

* change tx size tests to use addRemoteSync instead of validateTx (requested in pool request).

* core: minor tx slotting polishes, add slot tracking metric

Co-authored-by: Michael Riabzev <RiabzevMichael@gmail.com>
Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2020-01-10 11:40:03 +02:00
Péter Szilágyi
b5c4ea56b8 Merge pull request #20540 from holiman/verbose_panic
core/state: add more verbosity to panic
2020-01-10 11:36:21 +02:00
Martin Holst Swende
fc392395fb core/state: add more verbosity to panic 2020-01-10 10:12:32 +01:00
Péter Szilágyi
b211742e5f Revert "eth: refactor creation of EthAPIBackend (#20476)" (#20536)
This reverts commit a1bc0e3cb6.
2020-01-09 13:26:37 +02:00
Felix Lange
0218d7001d internal/testlog: print file+line number of log call in test log (#20528)
* internal/testlog: print file+line number of log call in test log

This changes the unit test logger to print the actual file and line
number of the logging call instead of "testlog.go:44".

Output of 'go test -v -run TestServerListen ./p2p' before this change:

    === RUN   TestServerListen
    --- PASS: TestServerListen (0.00s)
        testlog.go:44: DEBUG[01-08|15:16:31.651] UDP listener up         addr=127.0.0.1:62678
        testlog.go:44: DEBUG[01-08|15:16:31.651] TCP listener up         addr=127.0.0.1:62678
        testlog.go:44: TRACE[01-08|15:16:31.652] Accepted connection     addr=127.0.0.1:62679

And after:

    === RUN   TestServerListen
    --- PASS: TestServerListen (0.00s)
        server.go:868: DEBUG[01-08|15:25:35.679] TCP listener up         addr=127.0.0.1:62712
        server.go:557: DEBUG[01-08|15:25:35.679] UDP listener up         addr=127.0.0.1:62712
        server.go:912: TRACE[01-08|15:25:35.680] Accepted connection     addr=127.0.0.1:62713

* internal/testlog: document use of t.Helper
2020-01-08 17:11:51 +02:00
gary rong
4d663d57d6 les: fix request serving metrics (#20507) 2020-01-08 15:08:56 +02:00
Felix Lange
8a63f7f504 .travis.yml: use latest macOS 10.14 image (#20526) 2020-01-08 11:25:01 +02:00
Guillaume Ballet
c49a4165d0 consensus/ethash: fix a typo and error message (#20503) 2020-01-07 18:19:21 +01:00
Jonathan Gimeno
a1bc0e3cb6 eth: refactor creation of EthAPIBackend (#20476) 2020-01-07 18:12:27 +01:00
wangxiang
a013f02df2 whisper/whisperv6: fix peer time.Ticker leak (#20520) 2020-01-07 18:08:22 +01:00
Marius van der Wijden
50be790869 README.md: Genoil fork has been discontinued (#20521) 2020-01-07 18:06:44 +01:00
Yole
9e0f934e2b cmd/geth: update copyright year (#20512)
Update copyright from 2013-2019 to 2013-2020
2020-01-07 11:56:16 +02:00
me020523
4f7b7f84ae add node.go unit test file node_test.go (#20028)
* add node.go unit test file node_test.go

* add node_test.go file license and rollback trie_test.go

* fix unused variable v

* trie: fix license year

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2020-01-07 10:31:20 +01:00
gary rong
c6285e6437 les/checkpointoracle: move oracle into its own package (#20508)
* les: move the checkpoint oracle into its own package

It's first step of refactor LES package. LES package
basically can be divided into LES client and LES server.
However both sides will use checkpoint package for
status retrieval and verification. So this PR moves
checkpoint oracle into a separate package

* les: address comments
2020-01-07 11:24:21 +02:00
Kumar Anirudha
35f95aef6f cmd/puppeth: change dashboard title to not use "testnet" (#20513) 2020-01-07 11:08:33 +02:00
Prince Sinha
7a509b4732 internal/ethapi: fix encoding of uncle headers and pending blocks (#20460)
Fixes #19024
Fixes #19332
2020-01-06 12:25:38 +01:00
Guillaume Ballet
433937fb42 cmd/geth: fix forked exe leak in console tests (#20480) 2020-01-06 12:07:21 +01:00
Chris Pacia
2eeb8dd271 rpc: add DialWebsocketWithDialer (#20471)
This commit intents to replicate the DialHTTPWithClient function which allows
creating a RPC Client using a custom dialer but for websockets.

We introduce a new DialWebsocketWithDialer function which allows the caller
to instantiate a new websocket client using a custom dialer.
2020-01-06 12:06:29 +01:00
Sylvain Laurent
b7cf41e4b3 accounts/abi: fix method constant flag for solidity 6.0 (#20482) 2020-01-06 12:03:26 +01:00
Felföldi Zsolt
3bb6815fc1 les: do not disconnect another server (#20453) 2019-12-25 02:06:00 +01:00
Gerald Nash
a67fe48b43 Change file extension of the ./tests/fuzzers README (#20474) 2019-12-20 13:19:01 +01:00
Ilan Gitter
93b1171316 accounts/abi/backends/simulated: add more API methods (#5) (#20208)
* Add more functionality to the sim (#5)

* backends: implement more of ethclient in sim

* backends: add BlockByNumber to simulated backend

* backends: make simulated progress function agree with syncprogress interface for client

* backends: add more tests

* backends: add more comments

* backends: fix sim for index in tx and add tests

* backends: add lock back to estimategas

* backends: goimports

* backends: go ci lint

* Add more functionality to the sim (#5)

* backends: implement more of ethclient in sim

* backends: add BlockByNumber to simulated backend

* backends: make simulated progress function agree with syncprogress interface for client

* backends: add more tests

* backends: add more comments

* backends: fix sim for index in tx and add tests

* backends: add lock back to estimategas

* backends: goimports

* backends: go ci lint

* assert errs
2019-12-20 11:33:32 +01:00
Jeff Wentworth
6ae9dc15cc [#20266] Fix bugs decoding integers and fixed bytes in indexed event fields (#20269)
* fix parseTopics() and add tests

* remove printf

* add ParseTopicsIntoMap() tests

* fix FixedBytesTy

* fix int and fixed bytes

* golint topics_test.go
2019-12-18 11:16:07 +01:00
Paweł Bylica
49cf000df7 cmd/evm: Add --bench flag for benchmarking (#20330)
The --bench flag uses the testing.B to execute the EVM bytecode many times and get the average exeuction time out of it.
2019-12-18 09:43:18 +01:00
Ryan Schneider
c4b7fdd27e eth, internal/web3ext: add optional first and last arguments to the admin_exportChain RPC. (#20107) 2019-12-17 12:10:14 +01:00
Guillaume Ballet
275cd4988d cmd/abigen: Sanitize vyper's combined json names (#20419)
* cmd/abigen: Sanitize vyper's combined json names

* Review feedback: handle full paths
2019-12-16 13:37:15 +01:00
Felix Lange
f51cf573b5 cmd/devp2p: implement AWS Route53 enrtree deployer (#20446) 2019-12-12 22:25:12 +01:00
Felix Lange
191364c350 p2p/dnsdisc: add enode.Iterator API (#20437)
* p2p/dnsdisc: add support for enode.Iterator

This changes the dnsdisc.Client API to support the enode.Iterator
interface.

* p2p/dnsdisc: rate-limit DNS requests

* p2p/dnsdisc: preserve linked trees across root updates

This improves the way links are handled when the link root changes.
Previously, sync would simply remove all links from the current tree and
garbage-collect all unreachable trees before syncing the new list of
links.

This behavior isn't great in certain cases: Consider a structure where
trees A, B, and C reference each other and D links to A. If D's link
root changed, the sync code would first remove trees A, B and C, only to
re-sync them later when the link to A was found again.

The fix for this problem is to track the current set of links in each
clientTree and removing old links only AFTER all links are synced.

* p2p/dnsdisc: deflake iterator test

* cmd/devp2p: adapt dnsClient to new p2p/dnsdisc API

* p2p/dnsdisc: tiny comment fix
2019-12-12 11:15:36 +02:00
Felix Lange
d90d1db609 eth/filters: remove use of event.TypeMux for pending logs (#20312) 2019-12-10 12:39:14 +01:00
Péter Szilágyi
b8bc9b3d8e Merge pull request #20444 from MariusVanDerWijden/patch-4
core: removed old invalid comment
2019-12-10 13:31:25 +02:00
Marius van der Wijden
f383eaa102 core: removed old invalid comment 2019-12-10 11:50:16 +01:00
Martin Holst Swende
cecc7230c0 tests/fuzzers: fuzzbuzz fuzzers for keystore, rlp, trie, whisper (#19910)
* fuzzers: fuzzers for keystore, rlp, trie, whisper (cred to @guidovranken)

* fuzzers: move fuzzers to testdata

* testdata/fuzzers: documentation

* testdata/fuzzers: corpus for rlp

* tests/fuzzers: fixup
2019-12-10 11:57:37 +02:00
Charing
4b40b5377b miner: add dependency for stress tests (#20436)
1.to build stress tests

Depends-On: 6269e5574c
2019-12-10 10:26:07 +02:00
Péter Szilágyi
370cb95b7f params: begin v1.9.10 release cycle 2019-12-06 11:53:25 +02:00
Péter Szilágyi
017449971e params: release Geth v1.9.9 2019-12-06 11:51:37 +02:00
Martin Holst Swende
bc01593afb consensus/ethash, params: eip-2384: bump difficulty bomb (#20347)
* consensus/ethash, params: implement eip-2384: bump difficulty bomb

* params: EIP 2384 compat checks

* consensus, params: add Muir Glacier block number (mainnet,ropsten) + official name

* core/forkid: forkid tests for muir glacier

* params/config: address review concerns

* params, core/forkid: review nitpicks

* cmd/geth,eth,les: add override option for muir glacier

* params: nit fix
2019-12-06 11:36:40 +02:00
Marius van der Wijden
c9dce0bfd7 p2p/enode: remove data race in sliceIter (#20421) 2019-12-05 22:16:35 +01:00
Péter Szilágyi
e78f631dfc Merge pull request #20428 from karalabe/cht-1.9.9
params: update CHTs for v1.9.9 release
2019-12-05 11:38:41 +02:00
Péter Szilágyi
6b6882f08b params: update CHTs for v1.9.9 release 2019-12-05 11:01:40 +02:00
Péter Szilágyi
c2d65d34d5 Merge pull request #20415 from karalabe/trie-dirty-cache-metrics
trie: track dirty cache metrics, track clean writes on commit
2019-12-02 12:51:00 +02:00
Péter Szilágyi
13ccf6016e trie: track dirty cache metrics, track clean writes on commit 2019-12-02 12:23:35 +02:00
Marius van der Wijden
7ce7c3967c accounts/abi/bind: fix destructive packing of *big.Int (#20412) 2019-12-02 10:29:25 +01:00
gary rong
fc7e0fe6c7 core, miner: remove PostChainEvents (#19396)
This change:

- removes the PostChainEvents method on core.BlockChain.
- sorts 'removed log' events by block number.
- fire the NewChainHead event if we inject a canonical block into the chain
  even if the entire insertion is not successful.
- guarantees correct event ordering in all cases.
2019-11-29 14:22:08 +01:00
Guillaume Ballet
5cc6e7a71e accounts/usbwallet: fix staticcheck warnings (#20372) 2019-11-29 11:47:14 +01:00
xinluyin
d556d39a2c internal/web3ext: add debug_accountRange (#20410) 2019-11-29 11:46:12 +01:00
Guillaume Ballet
54d332e1db accounts/scwallet: fix staticcheck warnings (#20370) 2019-11-29 11:42:53 +01:00
Guillaume Ballet
e0bf5f0ccb internal: fix staticcheck warnings (#20380) 2019-11-29 11:40:02 +01:00
Guillaume Ballet
1ff3d7c2d4 cmd/faucet, cmd/geth: fix staticcheck warnings (#20374) 2019-11-29 11:38:34 +01:00
gary rong
08611cfd75 trie: remove dead code (#20405) 2019-11-28 12:47:35 +02:00
Guillaume Ballet
9a529d64d1 log: fix staticcheck warnings (#20388) 2019-11-28 10:53:06 +01:00
Felix Lange
a91b704b01 consensus/ethash: refactor remote sealer (#20335)
The original idea behind this change was to remove a use of the
deprecated CancelRequest method. Simply removing it would've been an
option, but I couldn't resist and did a bit of a refactoring instead.

All remote sealing code was contained in a single giant function. Remote
sealing is now extracted into its own object, remoteSealer.
2019-11-28 10:51:57 +01:00
Péter Szilágyi
c9f28ca8e5 go: update fastcache to 1.5.3 (#20404)
deps: update fastcache to 1.5.3
2019-11-27 15:08:34 +02:00
Péter Szilágyi
58e33d9e5a Merge pull request #20403 from karalabe/fix-freezer-reinit
core/rawdb: fix reinit regression caused by the hash check PR
2019-11-27 15:05:58 +02:00
Martin Holst Swende
7800ba978d deps: update fastcache to 1.5.3 2019-11-27 13:46:07 +01:00
Péter Szilágyi
717f8a4e8f core/rawdb: fix reinit regression caused by the hash check PR 2019-11-27 14:41:47 +02:00
Guillaume Ballet
7b189d6f1f core: fix staticcheck warnings (#20384)
* core: fix staticcheck warnings

* fix goimports
2019-11-27 09:50:30 +01:00
Guillaume Ballet
c4844e9ee2 les: fix staticcheck warnings (#20371) 2019-11-27 09:49:41 +01:00
zaccoding
23c8c74131 cmd: fix command help messages in modules (#20203) 2019-11-26 11:46:39 +01:00
Péter Szilágyi
0676320169 params: begin v1.9.9 release cycle 2019-11-26 12:21:11 +02:00
Péter Szilágyi
d62e9b2857 params: release go-ethereum v1.9.8 2019-11-26 12:20:22 +02:00
Felföldi Zsolt
878e35bfde les: fix clientInfo deadlock (#20395) 2019-11-26 12:17:15 +02:00
Felix Lange
2e98706a99 p2p/discover: slow down lookups on empty table (#20389)
* p2p/discover: slow down lookups on empty table

* p2p/discover: wake from slowdown sleep when table is closed
2019-11-26 12:14:43 +02:00
Guillaume Ballet
8c1e8de839 accounts/keystore: fix staticcheck warnings (#20373)
* accounts/keystore: fix staticcheck warnings

* review feedback
2019-11-25 14:39:55 +01:00
gary rong
b26eedf9e9 accounts/abi/bind: avoid reclaring structs (#20381) 2019-11-25 14:03:22 +01:00
Felix Lange
44b41641f8 rlp: fix staticcheck warnings (#20368)
* rlp: fix staticcheck warnings

* rlp: fix ExampleDecode test
2019-11-25 14:41:53 +02:00
Péter Szilágyi
9ef90dbf30 Merge pull request #20385 from etclabscore/fix/version-cmd-networkid
cmd/geth: remove network id from version cmd
2019-11-25 13:51:22 +02:00
meows
d9d2a4eef9 cmd/geth: remove network id from version cmd
It was reflective only of the Default setting,
and not chain aware.
2019-11-25 06:17:45 -05:00
gary rong
9d67222f4e trie: replace bigcache with fastcache (#19971) 2019-11-25 10:58:15 +02:00
Guillaume Ballet
f5a68a40bf eth/tracers: fix staticcheck warnings (#20379) 2019-11-24 21:06:06 +01:00
Guillaume Ballet
f06ae5ca6a miner: fix staticcheck warnings (#20375) 2019-11-24 20:46:34 +01:00
Michael Forney
3a0480e07d core/asm: allow numbers in labels (#20362)
Numbers were already allowed when creating labels, just not when
referencing them.
2019-11-23 12:52:17 +01:00
Guillaume Ballet
5d21667587 tests, signer: remove staticcheck warnings (#20364) 2019-11-23 12:51:37 +01:00
Felix Lange
fdff182f11 p2p/discv5: add deprecation warning and remove unused code (#20367)
* p2p/discv5: add deprecation warning and remove unused code

* p2p/discv5: remove unused variables
2019-11-22 18:02:13 +02:00
Felix Lange
0abcf03fde trie: remove unused code (#20366) 2019-11-22 17:24:48 +02:00
Guillaume Ballet
58f2ce8671 metrics: fix issues reported by staticcheck (#20365) 2019-11-22 16:04:35 +01:00
Felix Lange
dd21f079e8 core/state: fix staticcheck warnings (#20357)
Also remove dependency on gopkg.in/check.v1 in tests.
2019-11-22 15:56:05 +01:00
Felix Lange
36a684ca1e accounts/abi: fix staticcheck warnings (#20358)
* accounts/abi: fix staticcheck warnings

* accounts/abi: restore unused field for test
2019-11-21 23:22:47 +02:00
Felix Lange
bcc1234778 accounts/abi/bind/backends: remove unused assignment (#20359) 2019-11-21 20:30:28 +02:00
Péter Szilágyi
c1db636fb3 Merge pull request #20360 from karalabe/ppa-fix-cigo-clean
build: skip go clean on PPA, messes with the module trick
2019-11-21 18:56:10 +02:00
Péter Szilágyi
5b558ad936 build: skip go clean on PPA, messes with the module trick 2019-11-21 18:50:58 +02:00
Felix Lange
b6d4f6b66e core/types: remove BlockBy sorting code (#20355) 2019-11-21 16:35:22 +02:00
Felix Lange
0ec5ab4175 common: improve GraphQL error messages (#20354) 2019-11-21 16:34:28 +02:00
Péter Szilágyi
0754100464 Merge pull request #20356 from karalabe/ppa-fix-cigo
build: pull in ci.go dependencies for the PPA builder
2019-11-21 16:24:07 +02:00
Péter Szilágyi
475ae8bd93 build: pull in ci.go dependencies for the PPA builder 2019-11-21 16:14:31 +02:00
Felix Lange
89ab8a74c0 go.mod: switch to Go modules (#20311)
* go.mod, vendor: switch to Go modules

* travis: explicitly enable go modules in Go 1.11 and 1.12

* accounts/abi/bind: switch binding test to go modules

* travis, build: aggregate and upload go mod dependencies for PPA

* go.mod: tidy up the modules to avoid xgo writes to go.sum

* build, internal/build: drop own file/folder copier

* travis: fake build ppa only for go module dependencies

* mobile: fix CopyFile switch to package cp

* build, travis: use ephemeral debsrc GOPATH to get mod deps
2019-11-21 14:51:53 +01:00
Felix Lange
72e62efc76 common/hexutil: improve GraphQL error messages (#20353) 2019-11-21 15:51:25 +02:00
Péter Szilágyi
f56f969dd3 Merge pull request #20350 from holiman/puppeth_ssh
cmd/puppeth: make ssh prompt more user-friendly
2019-11-21 15:08:10 +02:00
Martin Holst Swende
216ff5a952 cmd/puppeth: make ssh prompt more user-friendly 2019-11-21 13:18:12 +01:00
Péter Szilágyi
7be89a7a01 Merge pull request #20339 from etclabscore/fix/cmd-puppeth-blocknonce-type
cmd/puppeth: x-spec nonce data type, use types.BlockNonce
2019-11-21 13:34:33 +02:00
meows
59177bc8c0 cmd/puppeth: x-spec nonce data type, use types.BlockNonce
Refactors to use existing BlockNonce type instead of
hand-rolled bytes logic.
2019-11-20 10:26:31 -05:00
Péter Szilágyi
c5b46a79c1 Merge pull request #20338 from etclabscore/feat/statetests-dedupe-walk-refactor
tests: refactor TestState to dedupe walk callback
2019-11-20 16:24:03 +02:00
meowsbits
b4bc3b3c35 tests: enable TransactionTests Istanbul case (#20337) 2019-11-20 15:08:07 +01:00
Péter Szilágyi
75e029db8b build, travis: use ephemeral debsrc GOPATH to get mod deps 2019-11-20 14:57:33 +02:00
meows
b8ced9e00b tests: refactor TestState to dedupe walk callback
Minor refactoring.
2019-11-20 07:54:18 -05:00
Péter Szilágyi
f8790b9482 mobile: fix CopyFile switch to package cp 2019-11-20 14:42:36 +02:00
Péter Szilágyi
8bd5bb8918 travis: fake build ppa only for go module dependencies 2019-11-20 14:42:36 +02:00
Péter Szilágyi
a7dfaa0bda build, internal/build: drop own file/folder copier 2019-11-20 14:42:35 +02:00
Péter Szilágyi
e1dcea8bf0 go.mod: tidy up the modules to avoid xgo writes to go.sum 2019-11-20 14:42:34 +02:00
Péter Szilágyi
b3d6304f1e travis, build: aggregate and upload go mod dependencies for PPA 2019-11-20 14:42:33 +02:00
Péter Szilágyi
f4ec85486a accounts/abi/bind: switch binding test to go modules 2019-11-20 14:42:33 +02:00
Péter Szilágyi
dfdb204b48 travis: explicitly enable go modules in Go 1.11 and 1.12 2019-11-20 14:42:32 +02:00
Péter Szilágyi
15fb780de6 go.mod, vendor: switch to Go modules 2019-11-20 14:42:28 +02:00
Péter Szilágyi
3a4a3d080b Merge pull request #20261 from holiman/less_querying
internal/ethapi: don't query wallets at every execution of gas estimation
2019-11-20 12:49:13 +02:00
gary rong
b7ba944e88 cmd/puppeth: update chain spec of parity (#20241) 2019-11-20 12:46:35 +02:00
gary rong
9b59c75405 miner: fix data race in tests (#20310)
* miner: fix data race in tests

miner: fix linter

* miner: address comment
2019-11-20 12:36:41 +02:00
Felix Lange
f71e85b8e2 core: fix staticcheck warnings (#20323) 2019-11-20 10:53:01 +02:00
Felix Lange
8008c5b1fa rpc: remove 'exported or builtin' restriction for parameters (#20332)
* rpc: remove 'exported or builtin' restriction for parameters

There is no technial reason for this restriction because package reflect
can create values of any type. Requiring parameters and return values to
be exported causes a lot of noise in package exports.

* rpc: fix staticcheck warnings
2019-11-20 10:06:21 +02:00
Felix Lange
9c6cf960b4 internal/web3ext, les: update clique JS and make it work with the light client (#20318)
Also fix the input formatter on clique_getSnapshot and clique_getSigners
so that integers as well as hex number strings are accepted.
2019-11-19 18:22:04 +01:00
Felix Lange
df206d2513 p2p/simulations: fix staticcheck warnings (#20322) 2019-11-19 17:16:42 +01:00
Felix Lange
9e8cc00b73 p2p: remove unused code (#20325) 2019-11-19 17:16:08 +01:00
Felix Lange
ac5e28ea38 whisper/whisperv6: fix staticcheck warnings (#20328) 2019-11-19 17:14:00 +01:00
Guillaume Ballet
3b0f3483c4 .github: remove 'nonsense' from CODEOWNERS (#20329) 2019-11-19 17:13:42 +01:00
Felix Lange
7f70a70106 event: remove unused field 'closed' (#20324) 2019-11-19 16:00:32 +02:00
Felix Lange
94e8250983 cmd/wnode: remove uses of common.ToHex (#20327) 2019-11-19 15:55:48 +02:00
Felix Lange
c013192ba7 ethclient: remove use of common.ToHex (#20326) 2019-11-19 15:53:26 +02:00
Guillaume Ballet
0b6338321f travis: deactivate arm build during push (#20321) 2019-11-19 13:57:05 +01:00
gary rong
b9c90c5581 core/rawdb: check hash before return data from ancient db (#20195)
* core/rawdb: check hash before return data from ancient db

* core/rawdb: fix lint

* core/rawdb: calculate the hash in the fly
2019-11-19 12:32:57 +02:00
Felix Lange
5fefe39ba5 p2p/netutil: fix staticcheck warning (#20315) 2019-11-19 11:17:41 +02:00
Felix Lange
dfe891270a cmd/ethkey: fix file permissions in changepassword command (#20313)
Found by staticcheck.
2019-11-19 11:16:34 +02:00
Felix Lange
c5c5e0dbe8 consensus/clique: fix struct tags for status API (#20316)
Also unexport the status struct.
2019-11-18 18:14:59 +01:00
Martin Holst Swende
3f4a875bf6 consensus/clique: add clique_status API method (#20103)
This PR introduces clique_status which gives info about the health of
the clique network.

It's currently a bit PITA to find out how a clique network is
performing, and it can easily happen that sealers drop off -- and
everything is 'fine' until one more signer drops off, and the network
suddenly halts.

The new method provides the following stats:

- Which signers are currently active, and have signed blocks in the last
  N (set to 64) blocks?
- How many blocks has each signer signed?
- What is the difficulty in the last N blocks, compared to the
  theoretical maximum?
2019-11-18 17:03:57 +01:00
Felix Lange
a3d263dd3a cmd/clef: fix staticcheck warnings (#20314) 2019-11-18 16:38:54 +01:00
meowsbits
190fb8180a build: add test cmd flag -v for verbose logs (#20298)
Adds flags akin to -coverage flag enabling the test runner
to use go test's -v flag, signaling verbose test log output.
2019-11-18 15:48:20 +01:00
Guillaume Ballet
b02afb6b3d travis: use travis_wait for both install and build (#20309) 2019-11-18 15:34:17 +01:00
Felföldi Zsolt
422604b438 les: rename UpdateBalance to AddBalance and simplify return format (#20304) 2019-11-18 12:42:49 +01:00
meowsbits
57d697629d core: s/isEIP155/isHomestead/g (fix IntrinsicGas signature var name) (#20300)
* core: s/isEIP155/isEIP2/ (fix)

This signature variable name reflects a spec'd change
in gas cost for creating contracts as documented in EIP2 (Homestead HF).

https://github.com/ethereum/EIPs/blob/master/EIPS/eip-2.md#specification

* core: s/isEIP2/sIsHomestead/g

Use isHomestead since Homestead is what the caller
and rest of the code uses.
2019-11-18 11:41:49 +02:00
Guillaume Ballet
11d09fd3ba travis: remove traces and use travis_wait in ARM build (#20296)
* travis: remove debug traces

* travis: Add travis_wait to the test run

* travis: increase travis_wait time
2019-11-18 10:52:12 +02:00
Felix Lange
689486449d build: use golangci-lint (#20295)
* build: use golangci-lint

This changes build/ci.go to download and run golangci-lint instead
of gometalinter.

* core/state: fix unnecessary conversion

* p2p/simulations: fix lock copying (found by go vet)

* signer/core: fix unnecessary conversions

* crypto/ecies: remove unused function cmpPublic

* core/rawdb: remove unused function print

* core/state: remove unused function xTestFuzzCutter

* core/vm: disable TestWriteExpectedValues in a different way

* core/forkid: remove unused function checksum

* les: remove unused type proofsData

* cmd/utils: remove unused functions prefixedNames, prefixFor

* crypto/bn256: run goimports

* p2p/nat: fix goimports lint issue

* cmd/clef: avoid using unkeyed struct fields

* les: cancel context in testRequest

* rlp: delete unreachable code

* core: gofmt

* internal/build: simplify DownloadFile for Go 1.11 compatibility

* build: remove go test --short flag

* .travis.yml: disable build cache

* whisper/whisperv6: fix ineffectual assignment in TestWhisperIdentityManagement

* .golangci.yml: enable goconst and ineffassign linters

* build: print message when there are no lint issues

* internal/build: refactor download a bit
2019-11-18 10:49:17 +02:00
Felix Lange
7c4a4eb58a rpc, p2p/simulations: use github.com/gorilla/websocket (#20289)
* rpc: improve codec abstraction

rpc.ServerCodec is an opaque interface. There was only one way to get a
codec using existing APIs: rpc.NewJSONCodec. This change exports
newCodec (as NewFuncCodec) and NewJSONCodec (as NewCodec). It also makes
all codec methods non-public to avoid showing internals in godoc.

While here, remove codec options in tests because they are not
supported anymore.

* p2p/simulations: use github.com/gorilla/websocket

This package was the last remaining user of golang.org/x/net/websocket.
Migrating to the new library wasn't straightforward because it is no
longer possible to treat WebSocket connections as a net.Conn.

* vendor: delete golang.org/x/net/websocket

* rpc: fix godoc comments and run gofmt
2019-11-18 10:40:59 +02:00
Michael Forney
9e71f55bfa cmd/evm: Allow loading input from file (#20273)
Make it possible to load input from a file. Simlar to `--code` / `--codefile`, have `--input`/`--inputfile`.
2019-11-17 15:45:54 +01:00
Martin Holst Swende
51c3290bee internal/ethapi: don't query wallets at every execution of gas estimation 2019-11-17 15:10:55 +01:00
nebojsa94
738b51ae31 core/vm: fix tracer interface parameter name (#20294) 2019-11-15 10:52:33 +02:00
meowsbits
f03b2db7db params: finish sentence in comment (#20291) 2019-11-14 23:05:32 +02:00
Guillaume Ballet
49d1a032da build: gather info to investigate why builds fail on ARM (#20281) 2019-11-14 14:42:23 +01:00
Guillaume Ballet
765fe446cf whisper/whisperv6: fix staticcheck issues (#20288) 2019-11-14 10:09:16 +01:00
Felix Lange
afe0b65405 dashboard: remove the dashboard (#20279)
This removes the dashboard project. The dashboard was an experimental
browser UI for geth which displayed metrics and chain information in
real time. We are removing it because it has marginal utility and nobody
on the team can maintain it.

Removing the dashboard removes a lot of dependency code and shaves
6 MB off the geth binary size.
2019-11-14 10:04:16 +01:00
Felix Lange
987648b0ad cmd/faucet: use github.com/gorilla/websocket (#20283)
golang.org/x/net/websocket is unmaintained, and we have already
switched to using github.com/gorilla/websocket for package rpc.
2019-11-14 10:05:17 +02:00
Jorropo
9504c5c360 rpc: fix typo example code (#20284) 2019-11-14 10:03:41 +02:00
gary rong
f8a95d996f accounts/abi/bind, cmd/abigen: implement alias for abigen (#20244)
* accounts/abi/bind, cmd/abigen: implement alias for abigen

* accounts/abi/bind: minor fixes

* accounts/abi/bind: address comments

* cmd/abigen: address comments

* accounts/abi/bind: print error log when identifier collision

* accounts/abi/bind: address comments

* accounts/abi/bind: address comment
2019-11-14 08:26:10 +01:00
Felföldi Zsolt
bf5c6b29fa les: implement server priority API (#20070)
This PR implements the LES server RPC API. Methods for server
capacity, client balance and client priority management are provided.
2019-11-13 23:47:03 +01:00
Guillaume Ballet
22e3bbbf0a miner: increase worker test timeout (#20268)
TestEmptyWork* occasionally fails due to timeout. Increase the timeout.
2019-11-13 12:40:50 +01:00
Kurkó Mihály
4ea9b62b5c dashboard: send current block to the dashboard client (#19762)
This adds all dashboard changes from the last couple months.
We're about to remove the dashboard, but decided that we should
get all the recent work in first in case anyone wants to pick up this
project later on.

* cmd, dashboard, eth, p2p: send peer info to the dashboard
* dashboard: update npm packages, improve UI, rebase
* dashboard, p2p: remove println, change doc
* cmd, dashboard, eth, p2p: cleanup after review
* dashboard: send current block to the dashboard client
2019-11-13 12:13:13 +01:00
Rick
6f1a600f6c p2p: fix bug in TestPeerDisconnect (#20277) 2019-11-13 12:01:52 +01:00
Guillaume Ballet
de2259d27c travis: enable test suite on ARM64 (#20219)
* travis: Enable ARM support

* Include fixes from 20039

* Add a trace to debug the invalid lookup issue

* Try increasing the timeout to see if the arm test passes

* Investigate the resolver issue

* Increase arm64 timeout for clique test

* increase timeout in tests for arm64

* Only test the failing tests

* Review feedback: don't export epsilon

* Remove investigation tricks+include fjl's feeback

* Revert the retry ahead of using the mock resolver

* Fix rebase errors
2019-11-08 10:58:57 +02:00
Felix Lange
adf007dadc p2p/enode: mock DNS resolver in URL parsing test (#20252) 2019-11-07 16:40:37 +01:00
Péter Szilágyi
4b8f56cf98 params: begin v1.9.8 release cycle 2019-11-07 10:01:20 +02:00
2036 changed files with 14444 additions and 875107 deletions

6
.github/CODEOWNERS vendored
View File

@@ -16,8 +16,8 @@ light/ @zsfelfoldi @rjl493456442
mobile/ @karalabe @ligi
p2p/ @fjl @zsfelfoldi
rpc/ @fjl @holiman
p2p/simulations @zelig @nonsense @janos @justelad
p2p/protocols @zelig @nonsense @janos @justelad
p2p/testing @zelig @nonsense @janos @justelad
p2p/simulations @zelig @janos @justelad
p2p/protocols @zelig @janos @justelad
p2p/testing @zelig @janos @justelad
signer/ @holiman
whisper/ @gballet @gluk256

1
.gitignore vendored
View File

@@ -24,6 +24,7 @@ build/_vendor/pkg
# used by the Makefile
/build/_workspace/
/build/cache/
/build/bin/
/geth*.zip

50
.golangci.yml Normal file
View File

@@ -0,0 +1,50 @@
# This file configures github.com/golangci/golangci-lint.
run:
timeout: 2m
tests: true
# default is true. Enables skipping of directories:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
skip-dirs-use-default: true
skip-files:
- core/genesis_alloc.go
linters:
disable-all: true
enable:
- deadcode
- goconst
- goimports
- gosimple
- govet
- ineffassign
- misspell
# - staticcheck
- unconvert
# - unused
- varcheck
linters-settings:
gofmt:
simplify: true
goconst:
min-len: 3 # minimum length of string constant
min-occurrences: 6 # minimum number of occurrences
issues:
exclude-rules:
- path: crypto/blake2b/
linters:
- deadcode
- path: crypto/bn256/cloudflare
linters:
- deadcode
- path: p2p/discv5/
linters:
- deadcode
- path: core/vm/instructions_test.go
linters:
- goconst
- path: cmd/faucet/
linters:
- deadcode

View File

@@ -19,6 +19,8 @@ jobs:
os: linux
dist: xenial
go: 1.11.x
env:
- GO111MODULE=on
script:
- go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES
@@ -27,6 +29,8 @@ jobs:
os: linux
dist: xenial
go: 1.12.x
env:
- GO111MODULE=on
script:
- go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES
@@ -34,6 +38,17 @@ jobs:
# These are the latest Go versions.
- stage: build
os: linux
arch: amd64
dist: xenial
go: 1.13.x
script:
- go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES
- stage: build
if: type = pull_request
os: linux
arch: arm64
dist: xenial
go: 1.13.x
script:
@@ -42,6 +57,7 @@ jobs:
- stage: build
os: osx
osx_image: xcode11.3
go: 1.13.x
script:
- echo "Increase the maximum number of open file descriptors on macOS"
@@ -75,12 +91,9 @@ jobs:
- fakeroot
- python-bzrlib
- python-paramiko
cache:
directories:
- $HOME/.gobundle
script:
- 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>" -goversion 1.13.4 -gohash 95dbeab442ee2746b9acf0934c8e2fc26414a0565c008631b04addb8c02e7624 -gobundle $HOME/.gobundle/go.tar.gz
- go run build/ci.go debsrc -goversion 1.13.8 -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
# This builder does the Linux Azure uploads
- stage: build
@@ -170,7 +183,7 @@ jobs:
git:
submodules: false # avoid cloning ethereum/tests
before_install:
- curl https://dl.google.com/go/go1.13.linux-amd64.tar.gz | tar -xz
- curl https://dl.google.com/go/go1.13.8.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH
- export GOROOT=`pwd`/go
- export GOPATH=$HOME/go

View File

@@ -10,33 +10,34 @@
GOBIN = ./build/bin
GO ?= latest
GORUN = env GO111MODULE=on go run
geth:
build/env.sh go run build/ci.go install ./cmd/geth
$(GORUN) build/ci.go install ./cmd/geth
@echo "Done building."
@echo "Run \"$(GOBIN)/geth\" to launch geth."
all:
build/env.sh go run build/ci.go install
$(GORUN) build/ci.go install
android:
build/env.sh go run build/ci.go aar --local
$(GORUN) build/ci.go aar --local
@echo "Done building."
@echo "Import \"$(GOBIN)/geth.aar\" to use the library."
ios:
build/env.sh go run build/ci.go xcode --local
$(GORUN) build/ci.go xcode --local
@echo "Done building."
@echo "Import \"$(GOBIN)/Geth.framework\" to use the library."
test: all
build/env.sh go run build/ci.go test
$(GORUN) build/ci.go test
lint: ## Run linters.
build/env.sh go run build/ci.go lint
$(GORUN) build/ci.go lint
clean:
./build/clean_go_build_cache.sh
env GO111MODULE=on go clean -cache
rm -fr build/_workspace/pkg/ $(GOBIN)/*
# The devtools target installs tools required for 'go generate'.
@@ -63,12 +64,12 @@ geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm geth-linux-mips64 get
@ls -ld $(GOBIN)/geth-linux-*
geth-linux-386:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/386 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/386 -v ./cmd/geth
@echo "Linux 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep 386
geth-linux-amd64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/amd64 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/amd64 -v ./cmd/geth
@echo "Linux amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep amd64
@@ -77,42 +78,42 @@ geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-ar
@ls -ld $(GOBIN)/geth-linux-* | grep arm
geth-linux-arm-5:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-5 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-5 -v ./cmd/geth
@echo "Linux ARMv5 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-5
geth-linux-arm-6:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-6 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-6 -v ./cmd/geth
@echo "Linux ARMv6 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-6
geth-linux-arm-7:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm-7 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-7 -v ./cmd/geth
@echo "Linux ARMv7 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-7
geth-linux-arm64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/arm64 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm64 -v ./cmd/geth
@echo "Linux ARM64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm64
geth-linux-mips:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips --ldflags '-extldflags "-static"' -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips --ldflags '-extldflags "-static"' -v ./cmd/geth
@echo "Linux MIPS cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mips
geth-linux-mipsle:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mipsle --ldflags '-extldflags "-static"' -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mipsle --ldflags '-extldflags "-static"' -v ./cmd/geth
@echo "Linux MIPSle cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mipsle
geth-linux-mips64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips64 --ldflags '-extldflags "-static"' -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips64 --ldflags '-extldflags "-static"' -v ./cmd/geth
@echo "Linux MIPS64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mips64
geth-linux-mips64le:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=linux/mips64le --ldflags '-extldflags "-static"' -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips64le --ldflags '-extldflags "-static"' -v ./cmd/geth
@echo "Linux MIPS64le cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mips64le
@@ -121,12 +122,12 @@ geth-darwin: geth-darwin-386 geth-darwin-amd64
@ls -ld $(GOBIN)/geth-darwin-*
geth-darwin-386:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=darwin/386 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=darwin/386 -v ./cmd/geth
@echo "Darwin 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-* | grep 386
geth-darwin-amd64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=darwin/amd64 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=darwin/amd64 -v ./cmd/geth
@echo "Darwin amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-* | grep amd64
@@ -135,11 +136,11 @@ geth-windows: geth-windows-386 geth-windows-amd64
@ls -ld $(GOBIN)/geth-windows-*
geth-windows-386:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=windows/386 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=windows/386 -v ./cmd/geth
@echo "Windows 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-* | grep 386
geth-windows-amd64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --targets=windows/amd64 -v ./cmd/geth
$(GORUN) build/ci.go xgo -- --go=$(GO) --targets=windows/amd64 -v ./cmd/geth
@echo "Windows amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-* | grep amd64

View File

@@ -39,7 +39,7 @@ directory.
| **`geth`** | Our main Ethereum CLI client. It is the entry point into the Ethereum network (main-, test- or private net), capable of running as a full node (default), archive node (retaining all historical state) or a light node (retrieving data live). It can be used by other processes as a gateway into the Ethereum network via JSON RPC endpoints exposed on top of HTTP, WebSocket and/or IPC transports. `geth --help` and the [CLI Wiki page](https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options) for command line options. |
| `abigen` | Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages. It operates on plain [Ethereum contract ABIs](https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI) with expanded functionality if the contract bytecode is also available. However, it also accepts Solidity source files, making development much more streamlined. Please see our [Native DApps](https://github.com/ethereum/go-ethereum/wiki/Native-DApps:-Go-bindings-to-Ethereum-contracts) wiki page for details. |
| `bootnode` | Stripped down version of our Ethereum client implementation that only takes part in the network node discovery protocol, but does not run any of the higher level application protocols. It can be used as a lightweight bootstrap node to aid in finding peers in private networks. |
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow isolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug run`). |
| `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. |
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user-friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
| `puppeth` | a CLI wizard that aids in creating a new Ethereum network. |
@@ -294,7 +294,7 @@ also need to configure a miner to process transactions and create new blocks for
Mining on the public Ethereum network is a complex task as it's only feasible using GPUs,
requiring an OpenCL or CUDA enabled `ethminer` instance. For information on such a
setup, please consult the [EtherMining subreddit](https://www.reddit.com/r/EtherMining/)
and the [Genoil miner](https://github.com/Genoil/cpp-ethereum) repository.
and the [ethminer](https://github.com/ethereum-mining/ethminer) repository.
In a private network setting, however a single CPU miner instance is more than enough for
practical purposes as it can produce a stable stream of blocks at the correct intervals

View File

@@ -108,12 +108,13 @@ func (abi ABI) UnpackIntoMap(v map[string]interface{}, name string, data []byte)
// UnmarshalJSON implements json.Unmarshaler interface
func (abi *ABI) UnmarshalJSON(data []byte) error {
var fields []struct {
Type string
Name string
Constant bool
Anonymous bool
Inputs []Argument
Outputs []Argument
Type string
Name string
Constant bool
StateMutability string
Anonymous bool
Inputs []Argument
Outputs []Argument
}
if err := json.Unmarshal(data, &fields); err != nil {
return err
@@ -134,10 +135,11 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
name = fmt.Sprintf("%s%d", field.Name, idx)
_, ok = abi.Methods[name]
}
isConst := field.Constant || field.StateMutability == "pure" || field.StateMutability == "view"
abi.Methods[name] = Method{
Name: name,
RawName: field.Name,
Const: field.Constant,
Const: isConst,
Inputs: field.Inputs,
Outputs: field.Outputs,
}

View File

@@ -927,7 +927,7 @@ func TestABI_MethodById(t *testing.T) {
}
b := fmt.Sprintf("%v", m2)
if a != b {
t.Errorf("Method %v (id %v) not 'findable' by id in ABI", name, common.ToHex(m.ID()))
t.Errorf("Method %v (id %x) not 'findable' by id in ABI", name, m.ID())
}
}
// Also test empty

View File

@@ -46,12 +46,17 @@ import (
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
var (
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
errBlockNumberUnsupported = errors.New("simulatedBackend cannot access blocks other than the latest block")
errBlockDoesNotExist = errors.New("block does not exist in blockchain")
errTransactionDoesNotExist = errors.New("transaction does not exist")
errGasEstimationFailed = errors.New("gas required exceeds allowance or always failing transaction")
)
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
// the background. Its main purpose is to allow easily testing contract bindings.
// Simulated backend implements the following interfaces:
// ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
// DeployBackend, GasEstimator, GasPricer, LogFilterer, PendingContractCaller, TransactionReader, and TransactionSender
type SimulatedBackend struct {
database ethdb.Database // In memory database to store our testing data
blockchain *core.BlockChain // Ethereum blockchain to handle the consensus
@@ -76,7 +81,7 @@ func NewSimulatedBackendWithDatabase(database ethdb.Database, alloc core.Genesis
database: database,
blockchain: blockchain,
config: genesis.Config,
events: filters.NewEventSystem(new(event.TypeMux), &filterBackend{database, blockchain}, false),
events: filters.NewEventSystem(&filterBackend{database, blockchain}, false),
}
backend.rollback()
return backend
@@ -122,15 +127,28 @@ func (b *SimulatedBackend) rollback() {
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database())
}
// stateByBlockNumber retrieves a state by a given blocknumber.
func (b *SimulatedBackend) stateByBlockNumber(ctx context.Context, blockNumber *big.Int) (*state.StateDB, error) {
if blockNumber == nil || blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) == 0 {
return b.blockchain.State()
}
block, err := b.BlockByNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
return b.blockchain.StateAt(block.Hash())
}
// CodeAt returns the code associated with a certain account in the blockchain.
func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
b.mu.Lock()
defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return nil, errBlockNumberUnsupported
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
statedb, _ := b.blockchain.State()
return statedb.GetCode(contract), nil
}
@@ -139,10 +157,11 @@ func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Addres
b.mu.Lock()
defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return nil, errBlockNumberUnsupported
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
statedb, _ := b.blockchain.State()
return statedb.GetBalance(contract), nil
}
@@ -151,10 +170,11 @@ func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address,
b.mu.Lock()
defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return 0, errBlockNumberUnsupported
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return 0, err
}
statedb, _ := b.blockchain.State()
return statedb.GetNonce(contract), nil
}
@@ -163,16 +183,20 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
b.mu.Lock()
defer b.mu.Unlock()
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return nil, errBlockNumberUnsupported
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
statedb, _ := b.blockchain.State()
val := statedb.GetState(contract, key)
return val[:], nil
}
// TransactionReceipt returns the receipt of a transaction.
func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
b.mu.Lock()
defer b.mu.Unlock()
receipt, _, _, _ := rawdb.ReadReceipt(b.database, txHash, b.config)
return receipt, nil
}
@@ -196,6 +220,115 @@ func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.
return nil, false, ethereum.NotFound
}
// BlockByHash retrieves a block based on the block hash
func (b *SimulatedBackend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
b.mu.Lock()
defer b.mu.Unlock()
if hash == b.pendingBlock.Hash() {
return b.pendingBlock, nil
}
block := b.blockchain.GetBlockByHash(hash)
if block != nil {
return block, nil
}
return nil, errBlockDoesNotExist
}
// BlockByNumber retrieves a block from the database by number, caching it
// (associated with its hash) if found.
func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
b.mu.Lock()
defer b.mu.Unlock()
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
return b.blockchain.CurrentBlock(), nil
}
block := b.blockchain.GetBlockByNumber(uint64(number.Int64()))
if block == nil {
return nil, errBlockDoesNotExist
}
return block, nil
}
// HeaderByHash returns a block header from the current canonical chain.
func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
b.mu.Lock()
defer b.mu.Unlock()
if hash == b.pendingBlock.Hash() {
return b.pendingBlock.Header(), nil
}
header := b.blockchain.GetHeaderByHash(hash)
if header == nil {
return nil, errBlockDoesNotExist
}
return header, nil
}
// HeaderByNumber returns a block header from the current canonical chain. If number is
// nil, the latest known header is returned.
func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (*types.Header, error) {
b.mu.Lock()
defer b.mu.Unlock()
if block == nil || block.Cmp(b.pendingBlock.Number()) == 0 {
return b.blockchain.CurrentHeader(), nil
}
return b.blockchain.GetHeaderByNumber(uint64(block.Int64())), nil
}
// TransactionCount returns the number of transactions in a given block
func (b *SimulatedBackend) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
b.mu.Lock()
defer b.mu.Unlock()
if blockHash == b.pendingBlock.Hash() {
return uint(b.pendingBlock.Transactions().Len()), nil
}
block := b.blockchain.GetBlockByHash(blockHash)
if block == nil {
return uint(0), errBlockDoesNotExist
}
return uint(block.Transactions().Len()), nil
}
// TransactionInBlock returns the transaction for a specific block at a specific index
func (b *SimulatedBackend) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
b.mu.Lock()
defer b.mu.Unlock()
if blockHash == b.pendingBlock.Hash() {
transactions := b.pendingBlock.Transactions()
if uint(len(transactions)) < index+1 {
return nil, errTransactionDoesNotExist
}
return transactions[index], nil
}
block := b.blockchain.GetBlockByHash(blockHash)
if block == nil {
return nil, errBlockDoesNotExist
}
transactions := block.Transactions()
if uint(len(transactions)) < index+1 {
return nil, errTransactionDoesNotExist
}
return transactions[index], nil
}
// PendingCodeAt returns the code associated with an account in the pending state.
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
b.mu.Lock()
@@ -419,10 +552,38 @@ func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethere
}), nil
}
// SubscribeNewHead returns an event subscription for a new header
func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
// subscribe to a new head
sink := make(chan *types.Header)
sub := b.events.SubscribeNewHeads(sink)
return event.NewSubscription(func(quit <-chan struct{}) error {
defer sub.Unsubscribe()
for {
select {
case head := <-sink:
select {
case ch <- head:
case err := <-sub.Err():
return err
case <-quit:
return nil
}
case err := <-sub.Err():
return err
case <-quit:
return nil
}
}
}), nil
}
// AdjustTime adds a time shift to the simulated clock.
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
b.mu.Lock()
defer b.mu.Unlock()
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() {
block.AddTx(tx)
@@ -502,22 +663,34 @@ func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*ty
}
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
return nullSubscription()
}
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
return fb.bc.SubscribeChainEvent(ch)
}
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
return fb.bc.SubscribeRemovedLogsEvent(ch)
}
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
return fb.bc.SubscribeLogsEvent(ch)
}
func (fb *filterBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
return nullSubscription()
}
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
panic("not supported")
}
func nullSubscription() event.Subscription {
return event.NewSubscription(func(quit <-chan struct{}) error {
<-quit
return nil
})
}
func (fb *filterBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription {
return fb.bc.SubscribeChainEvent(ch)
}
func (fb *filterBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription {
return fb.bc.SubscribeRemovedLogsEvent(ch)
}
func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
return fb.bc.SubscribeLogsEvent(ch)
}
func (fb *filterBackend) BloomStatus() (uint64, uint64) { return 4096, 0 }
func (fb *filterBackend) ServiceFilter(ctx context.Context, ms *bloombits.MatcherSession) {
panic("not supported")
}

View File

@@ -14,20 +14,24 @@
// 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 backends_test
package backends
import (
"bytes"
"context"
"math/big"
"strings"
"testing"
"time"
ethereum "github.com/ethereum/go-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/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
func TestSimulatedBackend(t *testing.T) {
@@ -37,7 +41,7 @@ func TestSimulatedBackend(t *testing.T) {
genAlloc := make(core.GenesisAlloc)
genAlloc[auth.From] = core.GenesisAccount{Balance: big.NewInt(9223372036854775807)}
sim := backends.NewSimulatedBackend(genAlloc, gasLimit)
sim := NewSimulatedBackend(genAlloc, gasLimit)
defer sim.Close()
// should return an error if the tx is not found
@@ -72,12 +76,767 @@ func TestSimulatedBackend(t *testing.T) {
}
sim.Commit()
tx, isPending, err = sim.TransactionByHash(context.Background(), txHash)
_, isPending, err = sim.TransactionByHash(context.Background(), txHash)
if err != nil {
t.Fatalf("error getting transaction with hash: %v", txHash.String())
}
if isPending {
t.Fatal("transaction should not have pending status")
}
}
var testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
// the following is based on this contract:
// contract T {
// event received(address sender, uint amount, bytes memo);
// event receivedAddr(address sender);
//
// function receive(bytes calldata memo) external payable returns (string memory res) {
// emit received(msg.sender, msg.value, memo);
// emit receivedAddr(msg.sender);
// return "hello world";
// }
// }
const abiJSON = `[ { "constant": false, "inputs": [ { "name": "memo", "type": "bytes" } ], "name": "receive", "outputs": [ { "name": "res", "type": "string" } ], "payable": true, "stateMutability": "payable", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" }, { "indexed": false, "name": "amount", "type": "uint256" }, { "indexed": false, "name": "memo", "type": "bytes" } ], "name": "received", "type": "event" }, { "anonymous": false, "inputs": [ { "indexed": false, "name": "sender", "type": "address" } ], "name": "receivedAddr", "type": "event" } ]`
const abiBin = `0x608060405234801561001057600080fd5b506102a0806100206000396000f3fe60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029`
const deployedCode = `60806040526004361061003b576000357c010000000000000000000000000000000000000000000000000000000090048063a69b6ed014610040575b600080fd5b6100b76004803603602081101561005657600080fd5b810190808035906020019064010000000081111561007357600080fd5b82018360208201111561008557600080fd5b803590602001918460018302840111640100000000831117156100a757600080fd5b9091929391929390505050610132565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100f75780820151818401526020810190506100dc565b50505050905090810190601f1680156101245780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b60607f75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed33348585604051808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001848152602001806020018281038252848482818152602001925080828437600081840152601f19601f8201169050808301925050509550505050505060405180910390a17f46923992397eac56cf13058aced2a1871933622717e27b24eabc13bf9dd329c833604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a16040805190810160405280600b81526020017f68656c6c6f20776f726c6400000000000000000000000000000000000000000081525090509291505056fea165627a7a72305820ff0c57dad254cfeda48c9cfb47f1353a558bccb4d1bc31da1dae69315772d29e0029`
// expected return value contains "hello world"
var expectedReturn = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
func TestNewSimulatedBackend(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: expectedBal},
}, 10000000,
)
defer sim.Close()
if sim.config != params.AllEthashProtocolChanges {
t.Errorf("expected sim config to equal params.AllEthashProtocolChanges, got %v", sim.config)
}
if sim.blockchain.Config() != params.AllEthashProtocolChanges {
t.Errorf("expected sim blockchain config to equal params.AllEthashProtocolChanges, got %v", sim.config)
}
statedb, _ := sim.blockchain.State()
bal := statedb.GetBalance(testAddr)
if bal.Cmp(expectedBal) != 0 {
t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
}
}
func TestSimulatedBackend_AdjustTime(t *testing.T) {
sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000,
)
defer sim.Close()
prevTime := sim.pendingBlock.Time()
err := sim.AdjustTime(time.Second)
if err != nil {
t.Error(err)
}
newTime := sim.pendingBlock.Time()
if newTime-prevTime != uint64(time.Second.Seconds()) {
t.Errorf("adjusted time not equal to a second. prev: %v, new: %v", prevTime, newTime)
}
}
func TestSimulatedBackend_BalanceAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: expectedBal},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
bal, err := sim.BalanceAt(bgCtx, testAddr, nil)
if err != nil {
t.Error(err)
}
if bal.Cmp(expectedBal) != 0 {
t.Errorf("expected balance for test address not received. expected: %v actual: %v", expectedBal, bal)
}
}
func TestSimulatedBackend_BlockByHash(t *testing.T) {
sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
block, err := sim.BlockByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get recent block: %v", err)
}
blockByHash, err := sim.BlockByHash(bgCtx, block.Hash())
if err != nil {
t.Errorf("could not get recent block: %v", err)
}
if block.Hash() != blockByHash.Hash() {
t.Errorf("did not get expected block")
}
}
func TestSimulatedBackend_BlockByNumber(t *testing.T) {
sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
block, err := sim.BlockByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get recent block: %v", err)
}
if block.NumberU64() != 0 {
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
}
// create one block
sim.Commit()
block, err = sim.BlockByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get recent block: %v", err)
}
if block.NumberU64() != 1 {
t.Errorf("did not get most recent block, instead got block number %v", block.NumberU64())
}
blockByNumber, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
if err != nil {
t.Errorf("could not get block by number: %v", err)
}
if blockByNumber.Hash() != block.Hash() {
t.Errorf("did not get the same block with height of 1 as before")
}
}
func TestSimulatedBackend_NonceAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
nonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(0))
if err != nil {
t.Errorf("could not get nonce for test addr: %v", err)
}
if nonce != uint64(0) {
t.Errorf("received incorrect nonce. expected 0, got %v", nonce)
}
// create a signed transaction to send
tx := types.NewTransaction(nonce, testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
// send tx to simulated backend
err = sim.SendTransaction(bgCtx, signedTx)
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
sim.Commit()
newNonce, err := sim.NonceAt(bgCtx, testAddr, big.NewInt(1))
if err != nil {
t.Errorf("could not get nonce for test addr: %v", err)
}
if newNonce != nonce+uint64(1) {
t.Errorf("received incorrect nonce. expected 1, got %v", nonce)
}
}
func TestSimulatedBackend_SendTransaction(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
// create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
// send tx to simulated backend
err = sim.SendTransaction(bgCtx, signedTx)
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
sim.Commit()
block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
if err != nil {
t.Errorf("could not get block at height 1: %v", err)
}
if signedTx.Hash() != block.Transactions()[0].Hash() {
t.Errorf("did not commit sent transaction. expected hash %v got hash %v", block.Transactions()[0].Hash(), signedTx.Hash())
}
}
func TestSimulatedBackend_TransactionByHash(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
// create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
// send tx to simulated backend
err = sim.SendTransaction(bgCtx, signedTx)
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
// ensure tx is committed pending
receivedTx, pending, err := sim.TransactionByHash(bgCtx, signedTx.Hash())
if err != nil {
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
}
if !pending {
t.Errorf("expected transaction to be in pending state")
}
if receivedTx.Hash() != signedTx.Hash() {
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
}
sim.Commit()
// ensure tx is not and committed pending
receivedTx, pending, err = sim.TransactionByHash(bgCtx, signedTx.Hash())
if err != nil {
t.Errorf("could not get transaction by hash %v: %v", signedTx.Hash(), err)
}
if pending {
t.Errorf("expected transaction to not be in pending state")
}
if receivedTx.Hash() != signedTx.Hash() {
t.Errorf("did not received committed transaction. expected hash %v got hash %v", signedTx.Hash(), receivedTx.Hash())
}
}
func TestSimulatedBackend_EstimateGas(t *testing.T) {
sim := NewSimulatedBackend(
core.GenesisAlloc{}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
gas, err := sim.EstimateGas(bgCtx, ethereum.CallMsg{
From: testAddr,
To: &testAddr,
Value: big.NewInt(1000),
Data: []byte{},
})
if err != nil {
t.Errorf("could not estimate gas: %v", err)
}
if gas != params.TxGas {
t.Errorf("expected 21000 gas cost for a transaction got %v", gas)
}
}
func TestSimulatedBackend_HeaderByHash(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
header, err := sim.HeaderByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get recent block: %v", err)
}
headerByHash, err := sim.HeaderByHash(bgCtx, header.Hash())
if err != nil {
t.Errorf("could not get recent block: %v", err)
}
if header.Hash() != headerByHash.Hash() {
t.Errorf("did not get expected block")
}
}
func TestSimulatedBackend_HeaderByNumber(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
latestBlockHeader, err := sim.HeaderByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get header for tip of chain: %v", err)
}
if latestBlockHeader == nil {
t.Errorf("received a nil block header")
}
if latestBlockHeader.Number.Uint64() != uint64(0) {
t.Errorf("expected block header number 0, instead got %v", latestBlockHeader.Number.Uint64())
}
sim.Commit()
latestBlockHeader, err = sim.HeaderByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get header for blockheight of 1: %v", err)
}
blockHeader, err := sim.HeaderByNumber(bgCtx, big.NewInt(1))
if err != nil {
t.Errorf("could not get header for blockheight of 1: %v", err)
}
if blockHeader.Hash() != latestBlockHeader.Hash() {
t.Errorf("block header and latest block header are not the same")
}
if blockHeader.Number.Int64() != int64(1) {
t.Errorf("did not get blockheader for block 1. instead got block %v", blockHeader.Number.Int64())
}
block, err := sim.BlockByNumber(bgCtx, big.NewInt(1))
if err != nil {
t.Errorf("could not get block for blockheight of 1: %v", err)
}
if block.Hash() != blockHeader.Hash() {
t.Errorf("block hash and block header hash do not match. expected %v, got %v", block.Hash(), blockHeader.Hash())
}
}
func TestSimulatedBackend_TransactionCount(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
currentBlock, err := sim.BlockByNumber(bgCtx, nil)
if err != nil || currentBlock == nil {
t.Error("could not get current block")
}
count, err := sim.TransactionCount(bgCtx, currentBlock.Hash())
if err != nil {
t.Error("could not get current block's transaction count")
}
if count != 0 {
t.Errorf("expected transaction count of %v does not match actual count of %v", 0, count)
}
// create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
// send tx to simulated backend
err = sim.SendTransaction(bgCtx, signedTx)
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
sim.Commit()
lastBlock, err := sim.BlockByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get header for tip of chain: %v", err)
}
count, err = sim.TransactionCount(bgCtx, lastBlock.Hash())
if err != nil {
t.Error("could not get current block's transaction count")
}
if count != 1 {
t.Errorf("expected transaction count of %v does not match actual count of %v", 1, count)
}
}
func TestSimulatedBackend_TransactionInBlock(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
transaction, err := sim.TransactionInBlock(bgCtx, sim.pendingBlock.Hash(), uint(0))
if err == nil && err != errTransactionDoesNotExist {
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
}
if transaction != nil {
t.Errorf("expected transaction to be nil but received %v", transaction)
}
// expect pending nonce to be 0 since account has not been used
pendingNonce, err := sim.PendingNonceAt(bgCtx, testAddr)
if err != nil {
t.Errorf("did not get the pending nonce: %v", err)
}
if pendingNonce != uint64(0) {
t.Errorf("expected pending nonce of 0 got %v", pendingNonce)
}
// create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
// send tx to simulated backend
err = sim.SendTransaction(bgCtx, signedTx)
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
sim.Commit()
lastBlock, err := sim.BlockByNumber(bgCtx, nil)
if err != nil {
t.Errorf("could not get header for tip of chain: %v", err)
}
transaction, err = sim.TransactionInBlock(bgCtx, lastBlock.Hash(), uint(1))
if err == nil && err != errTransactionDoesNotExist {
t.Errorf("expected a transaction does not exist error to be received but received %v", err)
}
if transaction != nil {
t.Errorf("expected transaction to be nil but received %v", transaction)
}
transaction, err = sim.TransactionInBlock(bgCtx, lastBlock.Hash(), uint(0))
if err != nil {
t.Errorf("could not get transaction in the lastest block with hash %v: %v", lastBlock.Hash().String(), err)
}
if signedTx.Hash().String() != transaction.Hash().String() {
t.Errorf("received transaction that did not match the sent transaction. expected hash %v, got hash %v", signedTx.Hash().String(), transaction.Hash().String())
}
}
func TestSimulatedBackend_PendingNonceAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
// expect pending nonce to be 0 since account has not been used
pendingNonce, err := sim.PendingNonceAt(bgCtx, testAddr)
if err != nil {
t.Errorf("did not get the pending nonce: %v", err)
}
if pendingNonce != uint64(0) {
t.Errorf("expected pending nonce of 0 got %v", pendingNonce)
}
// create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
// send tx to simulated backend
err = sim.SendTransaction(bgCtx, signedTx)
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
// expect pending nonce to be 1 since account has submitted one transaction
pendingNonce, err = sim.PendingNonceAt(bgCtx, testAddr)
if err != nil {
t.Errorf("did not get the pending nonce: %v", err)
}
if pendingNonce != uint64(1) {
t.Errorf("expected pending nonce of 1 got %v", pendingNonce)
}
// make a new transaction with a nonce of 1
tx = types.NewTransaction(uint64(1), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
signedTx, err = types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
err = sim.SendTransaction(bgCtx, signedTx)
if err != nil {
t.Errorf("could not send tx: %v", err)
}
// expect pending nonce to be 2 since account now has two transactions
pendingNonce, err = sim.PendingNonceAt(bgCtx, testAddr)
if err != nil {
t.Errorf("did not get the pending nonce: %v", err)
}
if pendingNonce != uint64(2) {
t.Errorf("expected pending nonce of 2 got %v", pendingNonce)
}
}
func TestSimulatedBackend_TransactionReceipt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
}, 10000000,
)
defer sim.Close()
bgCtx := context.Background()
// create a signed transaction to send
tx := types.NewTransaction(uint64(0), testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
signedTx, err := types.SignTx(tx, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
// send tx to simulated backend
err = sim.SendTransaction(bgCtx, signedTx)
if err != nil {
t.Errorf("could not add tx to pending block: %v", err)
}
sim.Commit()
receipt, err := sim.TransactionReceipt(bgCtx, signedTx.Hash())
if err != nil {
t.Errorf("could not get transaction receipt: %v", err)
}
if receipt.ContractAddress != testAddr && receipt.TxHash != signedTx.Hash() {
t.Errorf("received receipt is not correct: %v", receipt)
}
}
func TestSimulatedBackend_SuggestGasPrice(t *testing.T) {
sim := NewSimulatedBackend(
core.GenesisAlloc{},
10000000,
)
defer sim.Close()
bgCtx := context.Background()
gasPrice, err := sim.SuggestGasPrice(bgCtx)
if err != nil {
t.Errorf("could not get gas price: %v", err)
}
if gasPrice.Uint64() != uint64(1) {
t.Errorf("gas price was not expected value of 1. actual: %v", gasPrice.Uint64())
}
}
func TestSimulatedBackend_PendingCodeAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
},
10000000,
)
defer sim.Close()
bgCtx := context.Background()
code, err := sim.CodeAt(bgCtx, testAddr, nil)
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
if len(code) != 0 {
t.Errorf("got code for account that does not have contract code")
}
parsed, err := abi.JSON(strings.NewReader(abiJSON))
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
auth := bind.NewKeyedTransactor(testKey)
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
if err != nil {
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
}
code, err = sim.PendingCodeAt(bgCtx, contractAddr)
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
if len(code) == 0 {
t.Errorf("did not get code for account that has contract code")
}
// ensure code received equals code deployed
if !bytes.Equal(code, common.FromHex(deployedCode)) {
t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code)
}
}
func TestSimulatedBackend_CodeAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
},
10000000,
)
defer sim.Close()
bgCtx := context.Background()
code, err := sim.CodeAt(bgCtx, testAddr, nil)
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
if len(code) != 0 {
t.Errorf("got code for account that does not have contract code")
}
parsed, err := abi.JSON(strings.NewReader(abiJSON))
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
auth := bind.NewKeyedTransactor(testKey)
contractAddr, tx, contract, err := bind.DeployContract(auth, parsed, common.FromHex(abiBin), sim)
if err != nil {
t.Errorf("could not deploy contract: %v tx: %v contract: %v", err, tx, contract)
}
sim.Commit()
code, err = sim.CodeAt(bgCtx, contractAddr, nil)
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
if len(code) == 0 {
t.Errorf("did not get code for account that has contract code")
}
// ensure code received equals code deployed
if !bytes.Equal(code, common.FromHex(deployedCode)) {
t.Errorf("code received did not match expected deployed code:\n expected %v\n actual %v", common.FromHex(deployedCode), code)
}
}
// When receive("X") is called with sender 0x00... and value 1, it produces this tx receipt:
// receipt{status=1 cgas=23949 bloom=00000000004000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000040200000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 logs=[log: b6818c8064f645cd82d99b59a1a267d6d61117ef [75fd880d39c1daf53b6547ab6cb59451fc6452d27caa90e5b6649dd8293b9eed] 000000000000000000000000376c47978271565f56deb45495afa69e59c16ab200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000158 9ae378b6d4409eada347a5dc0c180f186cb62dc68fcc0f043425eb917335aa28 0 95d429d309bb9d753954195fe2d69bd140b4ae731b9b5b605c34323de162cf00 0]}
func TestSimulatedBackend_PendingAndCallContract(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := NewSimulatedBackend(
core.GenesisAlloc{
testAddr: {Balance: big.NewInt(10000000000)},
},
10000000,
)
defer sim.Close()
bgCtx := context.Background()
parsed, err := abi.JSON(strings.NewReader(abiJSON))
if err != nil {
t.Errorf("could not get code at test addr: %v", err)
}
contractAuth := bind.NewKeyedTransactor(testKey)
addr, _, _, err := bind.DeployContract(contractAuth, parsed, common.FromHex(abiBin), sim)
if err != nil {
t.Errorf("could not deploy contract: %v", err)
}
input, err := parsed.Pack("receive", []byte("X"))
if err != nil {
t.Errorf("could pack receive function on contract: %v", err)
}
// make sure you can call the contract in pending state
res, err := sim.PendingCallContract(bgCtx, ethereum.CallMsg{
From: testAddr,
To: &addr,
Data: input,
})
if err != nil {
t.Errorf("could not call receive method on contract: %v", err)
}
if len(res) == 0 {
t.Errorf("result of contract call was empty: %v", res)
}
// while comparing against the byte array is more exact, also compare against the human readable string for readability
if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
}
sim.Commit()
// make sure you can call the contract
res, err = sim.CallContract(bgCtx, ethereum.CallMsg{
From: testAddr,
To: &addr,
Data: input,
}, nil)
if err != nil {
t.Errorf("could not call receive method on contract: %v", err)
}
if len(res) == 0 {
t.Errorf("result of contract call was empty: %v", res)
}
if !bytes.Equal(res, expectedReturn) || !strings.Contains(string(res), "hello world") {
t.Errorf("response from calling contract was expected to be 'hello world' instead received %v", string(res))
}
}

View File

@@ -47,13 +47,17 @@ const (
// to be used as is in client code, but rather as an intermediate struct which
// enforces compile time type safety and naming convention opposed to having to
// manually maintain hard coded strings that break on runtime.
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string) (string, error) {
// Process each individual contract requested binding
contracts := make(map[string]*tmplContract)
func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]string, pkg string, lang Lang, libs map[string]string, aliases map[string]string) (string, error) {
var (
// contracts is the map of each individual contract requested binding
contracts = make(map[string]*tmplContract)
// Map used to flag each encountered library as such
isLib := make(map[string]struct{})
// structs is the map of all reclared structs shared by passed contracts.
structs = make(map[string]*tmplStruct)
// isLib is the map used to flag each encountered library as such
isLib = make(map[string]struct{})
)
for i := 0; i < len(types); i++ {
// Parse the actual ABI to generate the binding for
evmABI, err := abi.JSON(strings.NewReader(abis[i]))
@@ -73,13 +77,29 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
calls = make(map[string]*tmplMethod)
transacts = make(map[string]*tmplMethod)
events = make(map[string]*tmplEvent)
structs = make(map[string]*tmplStruct)
// identifiers are used to detect duplicated identifier of function
// and event. For all calls, transacts and events, abigen will generate
// corresponding bindings. However we have to ensure there is no
// identifier coliision in the bindings of these categories.
callIdentifiers = make(map[string]bool)
transactIdentifiers = make(map[string]bool)
eventIdentifiers = make(map[string]bool)
)
for _, original := range evmABI.Methods {
// Normalize the method for capital cases and non-anonymous inputs/outputs
normalized := original
normalized.Name = methodNormalizer[lang](original.Name)
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
// Ensure there is no duplicated identifier
var identifiers = callIdentifiers
if !original.Const {
identifiers = transactIdentifiers
}
if identifiers[normalizedName] {
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
}
identifiers[normalizedName] = true
normalized.Name = normalizedName
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs)
for j, input := range normalized.Inputs {
@@ -114,7 +134,14 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
}
// Normalize the event for capital cases and non-anonymous outputs
normalized := original
normalized.Name = methodNormalizer[lang](original.Name)
// Ensure there is no duplicated identifier
normalizedName := methodNormalizer[lang](alias(aliases, original.Name))
if eventIdentifiers[normalizedName] {
return "", fmt.Errorf("duplicated identifier \"%s\"(normalized \"%s\"), use --alias for renaming", original.Name, normalizedName)
}
eventIdentifiers[normalizedName] = true
normalized.Name = normalizedName
normalized.Inputs = make([]abi.Argument, len(original.Inputs))
copy(normalized.Inputs, original.Inputs)
@@ -144,7 +171,6 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
Transacts: transacts,
Events: events,
Libraries: make(map[string]string),
Structs: structs,
}
// Function 4-byte signatures are stored in the same sequence
// as types, if available.
@@ -176,6 +202,7 @@ func Bind(types []string, abis []string, bytecodes []string, fsigs []map[string]
Package: pkg,
Contracts: contracts,
Libraries: libs,
Structs: structs,
}
buffer := new(bytes.Buffer)
@@ -483,6 +510,15 @@ func namedTypeJava(javaKind string, solKind abi.Type) string {
}
}
// alias returns an alias of the given string based on the aliasing rules
// or returns itself if no rule is matched.
func alias(aliases map[string]string, n string) string {
if alias, exist := aliases[n]; exist {
return alias
}
return n
}
// methodNormalizer is a name transformer that modifies Solidity method names to
// conform to target language naming concentions.
var methodNormalizer = map[Lang]func(string) string{

View File

@@ -38,6 +38,7 @@ var bindTests = []struct {
tester string
fsigs []map[string]string
libs map[string]string
aliases map[string]string
types []string
}{
// Test that the binding is available in combined and separate forms too
@@ -61,6 +62,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that all the official sample contracts bind correctly
{
@@ -77,6 +79,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
`Crowdsale`,
@@ -92,6 +95,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
`DAO`,
@@ -107,6 +111,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that named and anonymous inputs are handled correctly
{
@@ -143,6 +148,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that named and anonymous outputs are handled correctly
{
@@ -182,6 +188,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that named, anonymous and indexed events are handled correctly
{
@@ -250,6 +257,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that contract interactions (deploy, transact and call) generate working code
{
@@ -311,6 +319,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that plain values can be properly returned and deserialized
{
@@ -356,6 +365,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that tuples can be properly returned and deserialized
{
@@ -401,6 +411,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that arrays/slices can be properly returned and deserialized.
// Only addresses are tested, remainder just compiled to keep the test small.
@@ -458,6 +469,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that anonymous default methods can be correctly invoked
{
@@ -508,6 +520,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that non-existent contracts are reported as such (though only simulator test)
{
@@ -547,6 +560,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that gas estimation works for contracts with weird gas mechanics too.
{
@@ -602,6 +616,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Test that constant functions can be called from an (optional) specified address
{
@@ -655,6 +670,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that methods and returns with underscores inside work correctly.
{
@@ -734,6 +750,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
// Tests that logs can be successfully filtered and decoded.
{
@@ -955,6 +972,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
`DeeplyNestedArray`,
@@ -1035,6 +1053,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
`CallbackParam`,
@@ -1076,6 +1095,7 @@ var bindTests = []struct {
},
nil,
nil,
nil,
}, {
`Tuple`,
`
@@ -1219,6 +1239,7 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
`UseLibrary`,
@@ -1287,6 +1308,7 @@ var bindTests = []struct {
map[string]string{
"b98c933f0a6ececcd167bd4f9d3299b1a0": "Math",
},
nil,
[]string{"UseLibrary", "Math"},
}, {
"Overload",
@@ -1298,7 +1320,7 @@ var bindTests = []struct {
event bar(uint256 i);
event bar(uint256 i, uint256 j);
function foo(uint256 i) public {
emit bar(i);
}
@@ -1362,7 +1384,7 @@ var bindTests = []struct {
if n != 3 {
t.Fatalf("Invalid bar0 event")
}
case <-time.NewTimer(100 * time.Millisecond).C:
case <-time.NewTimer(3 * time.Second).C:
t.Fatalf("Wait bar0 event timeout")
}
@@ -1373,7 +1395,7 @@ var bindTests = []struct {
if n != 1 {
t.Fatalf("Invalid bar event")
}
case <-time.NewTimer(100 * time.Millisecond).C:
case <-time.NewTimer(3 * time.Second).C:
t.Fatalf("Wait bar event timeout")
}
close(stopCh)
@@ -1381,6 +1403,187 @@ var bindTests = []struct {
nil,
nil,
nil,
nil,
},
{
"IdentifierCollision",
`
pragma solidity >=0.4.19 <0.6.0;
contract IdentifierCollision {
uint public _myVar;
function MyVar() public view returns (uint) {
return _myVar;
}
}
`,
[]string{"60806040523480156100115760006000fd5b50610017565b60c3806100256000396000f3fe608060405234801560105760006000fd5b506004361060365760003560e01c806301ad4d8714603c5780634ef1f0ad146058576036565b60006000fd5b60426074565b6040518082815260200191505060405180910390f35b605e607d565b6040518082815260200191505060405180910390f35b60006000505481565b60006000600050549050608b565b9056fea265627a7a7231582067c8d84688b01c4754ba40a2a871cede94ea1f28b5981593ab2a45b46ac43af664736f6c634300050c0032"},
[]string{`[{"constant":true,"inputs":[],"name":"MyVar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"_myVar","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}]`},
`
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/core"
`,
`
// Initialize test accounts
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
// Deploy registrar contract
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000)
defer sim.Close()
transactOpts := bind.NewKeyedTransactor(key)
_, _, _, err := DeployIdentifierCollision(transactOpts, sim)
if err != nil {
t.Fatalf("failed to deploy contract: %v", err)
}
`,
nil,
nil,
map[string]string{"_myVar": "pubVar"}, // alias MyVar to PubVar
nil,
},
{
"MultiContracts",
`
pragma solidity ^0.5.11;
pragma experimental ABIEncoderV2;
library ExternalLib {
struct SharedStruct{
uint256 f1;
bytes32 f2;
}
}
contract ContractOne {
function foo(ExternalLib.SharedStruct memory s) pure public {
// Do stuff
}
}
contract ContractTwo {
function bar(ExternalLib.SharedStruct memory s) pure public {
// Do stuff
}
}
`,
[]string{
`60806040523480156100115760006000fd5b50610017565b6101b5806100266000396000f3fe60806040523480156100115760006000fd5b50600436106100305760003560e01c80639d8a8ba81461003657610030565b60006000fd5b610050600480360361004b91908101906100d1565b610052565b005b5b5056610171565b6000813590506100698161013d565b92915050565b6000604082840312156100825760006000fd5b61008c60406100fb565b9050600061009c848285016100bc565b60008301525060206100b08482850161005a565b60208301525092915050565b6000813590506100cb81610157565b92915050565b6000604082840312156100e45760006000fd5b60006100f28482850161006f565b91505092915050565b6000604051905081810181811067ffffffffffffffff8211171561011f5760006000fd5b8060405250919050565b6000819050919050565b6000819050919050565b61014681610129565b811415156101545760006000fd5b50565b61016081610133565b8114151561016e5760006000fd5b50565bfea365627a7a72315820749274eb7f6c01010d5322af4e1668b0a154409eb7968bd6cae5524c7ed669bb6c6578706572696d656e74616cf564736f6c634300050c0040`,
`60806040523480156100115760006000fd5b50610017565b6101b5806100266000396000f3fe60806040523480156100115760006000fd5b50600436106100305760003560e01c8063db8ba08c1461003657610030565b60006000fd5b610050600480360361004b91908101906100d1565b610052565b005b5b5056610171565b6000813590506100698161013d565b92915050565b6000604082840312156100825760006000fd5b61008c60406100fb565b9050600061009c848285016100bc565b60008301525060206100b08482850161005a565b60208301525092915050565b6000813590506100cb81610157565b92915050565b6000604082840312156100e45760006000fd5b60006100f28482850161006f565b91505092915050565b6000604051905081810181811067ffffffffffffffff8211171561011f5760006000fd5b8060405250919050565b6000819050919050565b6000819050919050565b61014681610129565b811415156101545760006000fd5b50565b61016081610133565b8114151561016e5760006000fd5b50565bfea365627a7a723158209bc28ee7ea97c131a13330d77ec73b4493b5c59c648352da81dd288b021192596c6578706572696d656e74616cf564736f6c634300050c0040`,
`606c6026600b82828239805160001a6073141515601857fe5b30600052607381538281f350fe73000000000000000000000000000000000000000030146080604052600436106023575b60006000fdfea365627a7a72315820518f0110144f5b3de95697d05e456a064656890d08e6f9cff47f3be710cc46a36c6578706572696d656e74616cf564736f6c634300050c0040`,
},
[]string{
`[{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"f1","type":"uint256"},{"internalType":"bytes32","name":"f2","type":"bytes32"}],"internalType":"struct ExternalLib.SharedStruct","name":"s","type":"tuple"}],"name":"foo","outputs":[],"payable":false,"stateMutability":"pure","type":"function"}]`,
`[{"constant":true,"inputs":[{"components":[{"internalType":"uint256","name":"f1","type":"uint256"},{"internalType":"bytes32","name":"f2","type":"bytes32"}],"internalType":"struct ExternalLib.SharedStruct","name":"s","type":"tuple"}],"name":"bar","outputs":[],"payable":false,"stateMutability":"pure","type":"function"}]`,
`[]`,
},
`
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/core"
`,
`
key, _ := crypto.GenerateKey()
addr := crypto.PubkeyToAddress(key.PublicKey)
// Deploy registrar contract
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(1000000000)}}, 10000000)
defer sim.Close()
transactOpts := bind.NewKeyedTransactor(key)
_, _, c1, err := DeployContractOne(transactOpts, sim)
if err != nil {
t.Fatal("Failed to deploy contract")
}
sim.Commit()
err = c1.Foo(nil, ExternalLibSharedStruct{
F1: big.NewInt(100),
F2: [32]byte{0x01, 0x02, 0x03},
})
if err != nil {
t.Fatal("Failed to invoke function")
}
_, _, c2, err := DeployContractTwo(transactOpts, sim)
if err != nil {
t.Fatal("Failed to deploy contract")
}
sim.Commit()
err = c2.Bar(nil, ExternalLibSharedStruct{
F1: big.NewInt(100),
F2: [32]byte{0x01, 0x02, 0x03},
})
if err != nil {
t.Fatal("Failed to invoke function")
}
`,
nil,
nil,
nil,
[]string{"ContractOne", "ContractTwo", "ExternalLib"},
},
// Test the existence of the free retrieval calls
{
`PureAndView`,
`pragma solidity >=0.6.0;
contract PureAndView {
function PureFunc() public pure returns (uint) {
return 42;
}
function ViewFunc() public view returns (uint) {
return block.number;
}
}
`,
[]string{`608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c806376b5686a146037578063bb38c66c146053575b600080fd5b603d606f565b6040518082815260200191505060405180910390f35b60596077565b6040518082815260200191505060405180910390f35b600043905090565b6000602a90509056fea2646970667358221220d158c2ab7fdfce366a7998ec79ab84edd43b9815630bbaede2c760ea77f29f7f64736f6c63430006000033`},
[]string{`[{"inputs": [],"name": "PureFunc","outputs": [{"internalType": "uint256","name": "","type": "uint256"}],"stateMutability": "pure","type": "function"},{"inputs": [],"name": "ViewFunc","outputs": [{"internalType": "uint256","name": "","type": "uint256"}],"stateMutability": "view","type": "function"}]`},
`
"math/big"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
`,
`
// Generate a new random account and a funded simulator
key, _ := crypto.GenerateKey()
auth := bind.NewKeyedTransactor(key)
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000)}}, 10000000)
defer sim.Close()
// Deploy a tester contract and execute a structured call on it
_, _, pav, err := DeployPureAndView(auth, sim)
if err != nil {
t.Fatalf("Failed to deploy PureAndView contract: %v", err)
}
sim.Commit()
// This test the existence of the free retreiver call for view and pure functions
if num, err := pav.PureFunc(nil); err != nil {
t.Fatalf("Failed to call anonymous field retriever: %v", err)
} else if num.Cmp(big.NewInt(42)) != 0 {
t.Fatalf("Retrieved value mismatch: have %v, want %v", num, 42)
}
if num, err := pav.ViewFunc(nil); err != nil {
t.Fatalf("Failed to call anonymous field retriever: %v", err)
} else if num.Cmp(big.NewInt(1)) != 0 {
t.Fatalf("Retrieved value mismatch: have %v, want %v", num, 1)
}
`,
nil,
nil,
nil,
nil,
},
}
@@ -1412,7 +1615,7 @@ func TestGolangBindings(t *testing.T) {
types = []string{tt.name}
}
// Generate the binding and create a Go source file in the workspace
bind, err := Bind(types, tt.abi, tt.bytecode, tt.fsigs, "bindtest", LangGo, tt.libs)
bind, err := Bind(types, tt.abi, tt.bytecode, tt.fsigs, "bindtest", LangGo, tt.libs, tt.aliases)
if err != nil {
t.Fatalf("test %d: failed to generate binding: %v", i, err)
}
@@ -1436,6 +1639,18 @@ func TestGolangBindings(t *testing.T) {
t.Fatalf("test %d: failed to write tests: %v", i, err)
}
}
// Convert the package to go modules and use the current source for go-ethereum
moder := exec.Command(gocmd, "mod", "init", "bindtest")
moder.Dir = pkg
if out, err := moder.CombinedOutput(); err != nil {
t.Fatalf("failed to convert binding test to modules: %v\n%s", err, out)
}
pwd, _ := os.Getwd()
replacer := exec.Command(gocmd, "mod", "edit", "-replace", "github.com/ethereum/go-ethereum="+filepath.Join(pwd, "..", "..", "..")) // Repo root
replacer.Dir = pkg
if out, err := replacer.CombinedOutput(); err != nil {
t.Fatalf("failed to replace binding test dependency to current source tree: %v\n%s", err, out)
}
// Test the entire package and report any failures
cmd := exec.Command(gocmd, "test", "-v", "-count", "1")
cmd.Dir = pkg
@@ -1835,7 +2050,7 @@ public class Test {
},
}
for i, c := range cases {
binding, err := Bind([]string{c.name}, []string{c.abi}, []string{c.bytecode}, nil, "bindtest", LangJava, nil)
binding, err := Bind([]string{c.name}, []string{c.abi}, []string{c.bytecode}, nil, "bindtest", LangJava, nil, nil)
if err != nil {
t.Fatalf("test %d: failed to generate binding: %v", i, err)
}

View File

@@ -23,6 +23,7 @@ type tmplData struct {
Package string // Name of the package to place the generated file in
Contracts map[string]*tmplContract // List of contracts to generate into this file
Libraries map[string]string // Map the bytecode's link pattern to the library name
Structs map[string]*tmplStruct // Contract struct type definitions
}
// tmplContract contains the data needed to generate an individual contract binding.
@@ -36,8 +37,7 @@ type tmplContract struct {
Transacts map[string]*tmplMethod // Contract calls that write state data
Events map[string]*tmplEvent // Contract events accessors
Libraries map[string]string // Same as tmplData, but filtered to only keep what the contract needs
Structs map[string]*tmplStruct // Contract struct type definitions
Library bool
Library bool // Indicator whether the contract is a library
}
// tmplMethod is a wrapper around an abi.Method that contains a few preprocessed
@@ -108,8 +108,16 @@ var (
_ = event.NewSubscription
)
{{$structs := .Structs}}
{{range $structs}}
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
type {{.Name}} struct {
{{range $field := .Fields}}
{{$field.Name}} {{$field.Type}}{{end}}
}
{{end}}
{{range $contract := .Contracts}}
{{$structs := $contract.Structs}}
// {{.Type}}ABI is the input ABI used to generate the binding from.
const {{.Type}}ABI = "{{.InputABI}}"
@@ -285,14 +293,6 @@ var (
return _{{$contract.Type}}.Contract.contract.Transact(opts, method, params...)
}
{{range .Structs}}
// {{.Name}} is an auto generated low-level Go binding around an user-defined struct.
type {{.Name}} struct {
{{range $field := .Fields}}
{{$field.Name}} {{$field.Type}}{{end}}
}
{{end}}
{{range .Calls}}
// {{.Normalized.Name}} is a free data retrieval call binding the contract method 0x{{printf "%x" .Original.ID}}.
//
@@ -507,8 +507,8 @@ package {{.Package}};
import org.ethereum.geth.*;
import java.util.*;
{{$structs := .Structs}}
{{range $contract := .Contracts}}
{{$structs := $contract.Structs}}
{{if not .Library}}public {{end}}class {{.Type}} {
// ABI is the input ABI used to generate the binding from.
public final static String ABI = "{{.InputABI}}";

View File

@@ -178,6 +178,13 @@ func parseTopics(out interface{}, fields abi.Arguments, topics []common.Hash) er
case reflectBigInt:
num := new(big.Int).SetBytes(topics[0][:])
if arg.Type.T == abi.IntTy {
if num.Cmp(abi.MaxInt256) > 0 {
num.Add(abi.MaxUint256, big.NewInt(0).Neg(num))
num.Add(num, big.NewInt(1))
num.Neg(num)
}
}
field.Set(reflect.ValueOf(num))
default:
@@ -212,8 +219,7 @@ func parseTopicsIntoMap(out map[string]interface{}, fields abi.Arguments, topics
case abi.BoolTy:
out[arg.Name] = topics[0][common.HashLength-1] == 1
case abi.IntTy, abi.UintTy:
num := new(big.Int).SetBytes(topics[0][:])
out[arg.Name] = num
out[arg.Name] = abi.ReadInteger(arg.Type.T, arg.Type.Kind, topics[0].Bytes())
case abi.AddressTy:
var addr common.Address
copy(addr[:], topics[0][common.HashLength-common.AddressLength:])
@@ -221,7 +227,11 @@ func parseTopicsIntoMap(out map[string]interface{}, fields abi.Arguments, topics
case abi.HashTy:
out[arg.Name] = topics[0]
case abi.FixedBytesTy:
out[arg.Name] = topics[0][:]
array, err := abi.ReadFixedBytes(arg.Type, topics[0].Bytes())
if err != nil {
return err
}
out[arg.Name] = array
case abi.StringTy, abi.BytesTy, abi.SliceTy, abi.ArrayTy:
// Array types (including strings and bytes) have their keccak256 hashes stored in the topic- not a hash
// whose bytes can be decoded to the actual value- so the best we can do is retrieve that hash

View File

@@ -17,6 +17,7 @@
package bind
import (
"math/big"
"reflect"
"testing"
@@ -55,27 +56,44 @@ func TestMakeTopics(t *testing.T) {
}
}
func TestParseTopics(t *testing.T) {
type bytesStruct struct {
StaticBytes [5]byte
}
type args struct {
createObj func() interface{}
resultObj func() interface{}
resultMap func() map[string]interface{}
fields abi.Arguments
topics []common.Hash
}
type bytesStruct struct {
StaticBytes [5]byte
}
type int8Struct struct {
Int8Value int8
}
type int256Struct struct {
Int256Value *big.Int
}
type topicTest struct {
name string
args args
wantErr bool
}
func setupTopicsTests() []topicTest {
bytesType, _ := abi.NewType("bytes5", "", nil)
type args struct {
createObj func() interface{}
resultObj func() interface{}
fields abi.Arguments
topics []common.Hash
}
tests := []struct {
name string
args args
wantErr bool
}{
int8Type, _ := abi.NewType("int8", "", nil)
int256Type, _ := abi.NewType("int256", "", nil)
tests := []topicTest{
{
name: "support fixed byte types, right padded to 32 bytes",
args: args{
createObj: func() interface{} { return &bytesStruct{} },
resultObj: func() interface{} { return &bytesStruct{StaticBytes: [5]byte{1, 2, 3, 4, 5}} },
resultMap: func() map[string]interface{} {
return map[string]interface{}{"staticBytes": [5]byte{1, 2, 3, 4, 5}}
},
fields: abi.Arguments{abi.Argument{
Name: "staticBytes",
Type: bytesType,
@@ -87,7 +105,54 @@ func TestParseTopics(t *testing.T) {
},
wantErr: false,
},
{
name: "int8 with negative value",
args: args{
createObj: func() interface{} { return &int8Struct{} },
resultObj: func() interface{} { return &int8Struct{Int8Value: -1} },
resultMap: func() map[string]interface{} {
return map[string]interface{}{"int8Value": int8(-1)}
},
fields: abi.Arguments{abi.Argument{
Name: "int8Value",
Type: int8Type,
Indexed: true,
}},
topics: []common.Hash{
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
},
},
wantErr: false,
},
{
name: "int256 with negative value",
args: args{
createObj: func() interface{} { return &int256Struct{} },
resultObj: func() interface{} { return &int256Struct{Int256Value: big.NewInt(-1)} },
resultMap: func() map[string]interface{} {
return map[string]interface{}{"int256Value": big.NewInt(-1)}
},
fields: abi.Arguments{abi.Argument{
Name: "int256Value",
Type: int256Type,
Indexed: true,
}},
topics: []common.Hash{
{255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255},
},
},
wantErr: false,
},
}
return tests
}
func TestParseTopics(t *testing.T) {
tests := setupTopicsTests()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
createObj := tt.args.createObj()
@@ -101,3 +166,20 @@ func TestParseTopics(t *testing.T) {
})
}
}
func TestParseTopicsIntoMap(t *testing.T) {
tests := setupTopicsTests()
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
outMap := make(map[string]interface{})
if err := parseTopicsIntoMap(outMap, tt.args.fields, tt.args.topics); (err != nil) != tt.wantErr {
t.Errorf("parseTopicsIntoMap() error = %v, wantErr %v", err, tt.wantErr)
}
resultMap := tt.args.resultMap()
if !reflect.DeepEqual(outMap, resultMap) {
t.Errorf("parseTopicsIntoMap() = %v, want %v", outMap, resultMap)
}
})
}
}

View File

@@ -173,7 +173,7 @@ func TestEventTupleUnpack(t *testing.T) {
type EventTransferWithTag struct {
// this is valid because `value` is not exportable,
// so value is only unmarshalled into `Value1`.
value *big.Int
value *big.Int //lint:ignore U1000 unused field is part of test
Value1 *big.Int `abi:"value"`
}
@@ -354,40 +354,6 @@ func unpackTestEventData(dest interface{}, hexData string, jsonEvent []byte, ass
return a.Unpack(dest, "e", data)
}
/*
Taken from
https://github.com/ethereum/go-ethereum/pull/15568
*/
type testResult struct {
Values [2]*big.Int
Value1 *big.Int
Value2 *big.Int
}
type testCase struct {
definition string
want testResult
}
func (tc testCase) encoded(intType, arrayType Type) []byte {
var b bytes.Buffer
if tc.want.Value1 != nil {
val, _ := intType.pack(reflect.ValueOf(tc.want.Value1))
b.Write(val)
}
if !reflect.DeepEqual(tc.want.Values, [2]*big.Int{nil, nil}) {
val, _ := arrayType.pack(reflect.ValueOf(tc.want.Values))
b.Write(val)
}
if tc.want.Value2 != nil {
val, _ := intType.pack(reflect.ValueOf(tc.want.Value2))
b.Write(val)
}
return b.Bytes()
}
// TestEventUnpackIndexed verifies that indexed field will be skipped by event decoder.
func TestEventUnpackIndexed(t *testing.T) {
definition := `[{"name": "test", "type": "event", "inputs": [{"indexed": true, "name":"value1", "type":"uint8"},{"indexed": false, "name":"value2", "type":"uint8"}]}]`

View File

@@ -73,7 +73,7 @@ func packNum(value reflect.Value) []byte {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return U256(big.NewInt(value.Int()))
case reflect.Ptr:
return U256(value.Interface().(*big.Int))
return U256(new(big.Int).Set(value.Interface().(*big.Int)))
default:
panic("abi: fatal error")
}

View File

@@ -26,16 +26,18 @@ import (
)
var (
maxUint256 = big.NewInt(0).Add(
// MaxUint256 is the maximum value that can be represented by a uint256
MaxUint256 = big.NewInt(0).Add(
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil),
big.NewInt(-1))
maxInt256 = big.NewInt(0).Add(
// MaxInt256 is the maximum value that can be represented by a int256
MaxInt256 = big.NewInt(0).Add(
big.NewInt(0).Exp(big.NewInt(2), big.NewInt(255), nil),
big.NewInt(-1))
)
// reads the integer based on its kind
func readInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
// ReadInteger reads the integer based on its kind and returns the appropriate value
func ReadInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
switch kind {
case reflect.Uint8:
return b[len(b)-1]
@@ -62,8 +64,8 @@ func readInteger(typ byte, kind reflect.Kind, b []byte) interface{} {
return ret
}
if ret.Cmp(maxInt256) > 0 {
ret.Add(maxUint256, big.NewInt(0).Neg(ret))
if ret.Cmp(MaxInt256) > 0 {
ret.Add(MaxUint256, big.NewInt(0).Neg(ret))
ret.Add(ret, big.NewInt(1))
ret.Neg(ret)
}
@@ -102,8 +104,8 @@ func readFunctionType(t Type, word []byte) (funcTy [24]byte, err error) {
return
}
// through reflection, creates a fixed array to be read from
func readFixedBytes(t Type, word []byte) (interface{}, error) {
// ReadFixedBytes uses reflection to create a fixed array to be read from
func ReadFixedBytes(t Type, word []byte) (interface{}, error) {
if t.T != FixedBytesTy {
return nil, fmt.Errorf("abi: invalid type in call to make fixed byte array")
}
@@ -230,7 +232,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
case StringTy: // variable arrays are written at the end of the return bytes
return string(output[begin : begin+length]), nil
case IntTy, UintTy:
return readInteger(t.T, t.Kind, returnOutput), nil
return ReadInteger(t.T, t.Kind, returnOutput), nil
case BoolTy:
return readBool(returnOutput)
case AddressTy:
@@ -240,7 +242,7 @@ func toGoType(index int, t Type, output []byte) (interface{}, error) {
case BytesTy:
return output[begin : begin+length], nil
case FixedBytesTy:
return readFixedBytes(t, returnOutput)
return ReadFixedBytes(t, returnOutput)
case FunctionTy:
return readFunctionType(t, returnOutput)
default:

View File

@@ -123,6 +123,7 @@ func (ks keyStorePassphrase) StoreKey(filename string, key *Key, auth string) er
"Please file a ticket at:\n\n" +
"https://github.com/ethereum/go-ethereum/issues." +
"The error was : %s"
//lint:ignore ST1005 This is a message for the user
return fmt.Errorf(msg, tmpName, err)
}
}
@@ -237,7 +238,7 @@ func DecryptKey(keyjson []byte, auth string) (*Key, error) {
func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
if cryptoJson.Cipher != "aes-128-ctr" {
return nil, fmt.Errorf("Cipher not supported: %v", cryptoJson.Cipher)
return nil, fmt.Errorf("cipher not supported: %v", cryptoJson.Cipher)
}
mac, err := hex.DecodeString(cryptoJson.MAC)
if err != nil {
@@ -273,7 +274,7 @@ func DecryptDataV3(cryptoJson CryptoJSON, auth string) ([]byte, error) {
func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byte, keyId []byte, err error) {
if keyProtected.Version != version {
return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
return nil, nil, fmt.Errorf("version not supported: %v", keyProtected.Version)
}
keyId = uuid.Parse(keyProtected.Id)
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
@@ -335,13 +336,13 @@ func getKDFKey(cryptoJSON CryptoJSON, auth string) ([]byte, error) {
c := ensureInt(cryptoJSON.KDFParams["c"])
prf := cryptoJSON.KDFParams["prf"].(string)
if prf != "hmac-sha256" {
return nil, fmt.Errorf("Unsupported PBKDF2 PRF: %s", prf)
return nil, fmt.Errorf("unsupported PBKDF2 PRF: %s", prf)
}
key := pbkdf2.Key(authArray, salt, c, dkLen, sha256.New)
return key, nil
}
return nil, fmt.Errorf("Unsupported KDF: %s", cryptoJSON.KDF)
return nil, fmt.Errorf("unsupported KDF: %s", cryptoJSON.KDF)
}
// TODO: can we do without this when unmarshalling dynamic JSON?

View File

@@ -141,6 +141,11 @@ func (am *Manager) Wallets() []Wallet {
am.lock.RLock()
defer am.lock.RUnlock()
return am.walletsNoLock()
}
// walletsNoLock returns all registered wallets. Callers must hold am.lock.
func (am *Manager) walletsNoLock() []Wallet {
cpy := make([]Wallet, len(am.wallets))
copy(cpy, am.wallets)
return cpy
@@ -155,7 +160,7 @@ func (am *Manager) Wallet(url string) (Wallet, error) {
if err != nil {
return nil, err
}
for _, wallet := range am.Wallets() {
for _, wallet := range am.walletsNoLock() {
if wallet.URL() == parsed {
return wallet, nil
}

View File

@@ -71,7 +71,7 @@ func NewSecureChannelSession(card *pcsc.Card, keyData []byte) (*SecureChannelSes
cardPublic, ok := gen.Unmarshal(keyData)
if !ok {
return nil, fmt.Errorf("Could not unmarshal public key from card")
return nil, fmt.Errorf("could not unmarshal public key from card")
}
secret, err := gen.GenerateSharedSecret(private, cardPublic)
@@ -109,7 +109,7 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
cardChallenge := response.Data[32:64]
if !bytes.Equal(expectedCryptogram, cardCryptogram) {
return fmt.Errorf("Invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
return fmt.Errorf("invalid card cryptogram %v != %v", expectedCryptogram, cardCryptogram)
}
md.Reset()
@@ -132,7 +132,7 @@ func (s *SecureChannelSession) Pair(pairingPassword []byte) error {
// Unpair disestablishes an existing pairing.
func (s *SecureChannelSession) Unpair() error {
if s.PairingKey == nil {
return fmt.Errorf("Cannot unpair: not paired")
return fmt.Errorf("cannot unpair: not paired")
}
_, err := s.transmitEncrypted(claSCWallet, insUnpair, s.PairingIndex, 0, []byte{})
@@ -148,7 +148,7 @@ func (s *SecureChannelSession) Unpair() error {
// Open initializes the secure channel.
func (s *SecureChannelSession) Open() error {
if s.iv != nil {
return fmt.Errorf("Session already opened")
return fmt.Errorf("session already opened")
}
response, err := s.open()
@@ -185,11 +185,11 @@ func (s *SecureChannelSession) mutuallyAuthenticate() error {
return err
}
if response.Sw1 != 0x90 || response.Sw2 != 0x00 {
return fmt.Errorf("Got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
return fmt.Errorf("got unexpected response from MUTUALLY_AUTHENTICATE: 0x%x%x", response.Sw1, response.Sw2)
}
if len(response.Data) != scSecretLength {
return fmt.Errorf("Response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
return fmt.Errorf("response from MUTUALLY_AUTHENTICATE was %d bytes, expected %d", len(response.Data), scSecretLength)
}
return nil
@@ -222,7 +222,7 @@ func (s *SecureChannelSession) pair(p1 uint8, data []byte) (*responseAPDU, error
// transmitEncrypted sends an encrypted message, and decrypts and returns the response.
func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []byte) (*responseAPDU, error) {
if s.iv == nil {
return nil, fmt.Errorf("Channel not open")
return nil, fmt.Errorf("channel not open")
}
data, err := s.encryptAPDU(data)
@@ -261,14 +261,14 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
return nil, err
}
if !bytes.Equal(s.iv, rmac) {
return nil, fmt.Errorf("Invalid MAC in response")
return nil, fmt.Errorf("invalid MAC in response")
}
rapdu := &responseAPDU{}
rapdu.deserialize(plainData)
if rapdu.Sw1 != sw1Ok {
return nil, fmt.Errorf("Unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
return nil, fmt.Errorf("unexpected response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", cla, ins, rapdu.Sw1, rapdu.Sw2)
}
return rapdu, nil
@@ -277,7 +277,7 @@ func (s *SecureChannelSession) transmitEncrypted(cla, ins, p1, p2 byte, data []b
// encryptAPDU is an internal method that serializes and encrypts an APDU.
func (s *SecureChannelSession) encryptAPDU(data []byte) ([]byte, error) {
if len(data) > maxPayloadSize {
return nil, fmt.Errorf("Payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
return nil, fmt.Errorf("payload of %d bytes exceeds maximum of %d", len(data), maxPayloadSize)
}
data = pad(data, 0x80)
@@ -323,10 +323,10 @@ func unpad(data []byte, terminator byte) ([]byte, error) {
case terminator:
return data[:len(data)-i], nil
default:
return nil, fmt.Errorf("Expected end of padding, got %d", data[len(data)-i])
return nil, fmt.Errorf("expected end of padding, got %d", data[len(data)-i])
}
}
return nil, fmt.Errorf("Expected end of padding, got 0")
return nil, fmt.Errorf("expected end of padding, got 0")
}
// updateIV is an internal method that updates the initialization vector after

View File

@@ -167,7 +167,7 @@ func transmit(card *pcsc.Card, command *commandAPDU) (*responseAPDU, error) {
}
if response.Sw1 != sw1Ok {
return nil, fmt.Errorf("Unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
return nil, fmt.Errorf("unexpected insecure response status Cla=0x%x, Ins=0x%x, Sw=0x%x%x", command.Cla, command.Ins, response.Sw1, response.Sw2)
}
return response, nil
@@ -252,7 +252,7 @@ func (w *Wallet) release() error {
// with the wallet.
func (w *Wallet) pair(puk []byte) error {
if w.session.paired() {
return fmt.Errorf("Wallet already paired")
return fmt.Errorf("wallet already paired")
}
pairing, err := w.session.pair(puk)
if err != nil {
@@ -773,12 +773,12 @@ func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationP
// Look for the path in the URL
if account.URL.Scheme != w.Hub.scheme {
return nil, fmt.Errorf("Scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme)
return nil, fmt.Errorf("scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme)
}
parts := strings.SplitN(account.URL.Path, "/", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("Invalid URL format: %s", account.URL)
return nil, fmt.Errorf("invalid URL format: %s", account.URL)
}
if parts[0] != fmt.Sprintf("%x", w.PublicKey[1:3]) {
@@ -813,7 +813,7 @@ func (s *Session) pair(secret []byte) (smartcardPairing, error) {
// unpair deletes an existing pairing.
func (s *Session) unpair() error {
if !s.verified {
return fmt.Errorf("Unpair requires that the PIN be verified")
return fmt.Errorf("unpair requires that the PIN be verified")
}
return s.Channel.Unpair()
}
@@ -850,7 +850,7 @@ func (s *Session) paired() bool {
// authenticate uses an existing pairing to establish a secure channel.
func (s *Session) authenticate(pairing smartcardPairing) error {
if !bytes.Equal(s.Wallet.PublicKey, pairing.PublicKey) {
return fmt.Errorf("Cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
return fmt.Errorf("cannot pair using another wallet's pairing; %x != %x", s.Wallet.PublicKey, pairing.PublicKey)
}
s.Channel.PairingKey = pairing.PairingKey
s.Channel.PairingIndex = pairing.PairingIndex
@@ -879,6 +879,7 @@ func (s *Session) walletStatus() (*walletStatus, error) {
}
// derivationPath fetches the wallet's current derivation path from the card.
//lint:ignore U1000 needs to be added to the console interface
func (s *Session) derivationPath() (accounts.DerivationPath, error) {
response, err := s.Channel.transmitEncrypted(claSCWallet, insStatus, statusP1Path, 0, nil)
if err != nil {
@@ -993,12 +994,14 @@ func (s *Session) derive(path accounts.DerivationPath) (accounts.Account, error)
}
// keyExport contains information on an exported keypair.
//lint:ignore U1000 needs to be added to the console interface
type keyExport struct {
PublicKey []byte `asn1:"tag:0"`
PrivateKey []byte `asn1:"tag:1,optional"`
}
// publicKey returns the public key for the current derivation path.
//lint:ignore U1000 needs to be added to the console interface
func (s *Session) publicKey() ([]byte, error) {
response, err := s.Channel.transmitEncrypted(claSCWallet, insExportKey, exportP1Any, exportP2Pubkey, nil)
if err != nil {

View File

@@ -162,7 +162,8 @@ func (w *ledgerDriver) SignTx(path accounts.DerivationPath, tx *types.Transactio
return common.Address{}, nil, accounts.ErrWalletClosed
}
// Ensure the wallet is capable of signing the given transaction
if chainID != nil && w.version[0] <= 1 && w.version[1] <= 0 && w.version[2] <= 2 {
if chainID != nil && w.version[0] <= 1 && w.version[2] <= 2 {
//lint:ignore ST1005 brand name displayed on the console
return common.Address{}, nil, fmt.Errorf("Ledger v%d.%d.%d doesn't support signing this transaction, please update to v1.0.3 at least", w.version[0], w.version[1], w.version[2])
}
// All infos gathered and metadata checks out, request signing

View File

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

19
build/checksums.txt Normal file
View File

@@ -0,0 +1,19 @@
# This file contains sha256 checksums of optional build dependencies.
b13bf04633d4d8cf53226ebeaace8d4d2fd07ae6fa676d0844a688339debec34 go1.13.8.src.tar.gz
478994633b0f5121a7a8d4f368078093e21014fdc7fb2c0ceeae63668c13c5b6 golangci-lint-1.22.2-freebsd-amd64.tar.gz
fcf80824c21567eb0871055711bf9bdca91cf9a081122e2a45f1d11fed754600 golangci-lint-1.22.2-darwin-amd64.tar.gz
cda85c72fc128b2ea0ae05baea7b91172c63aea34064829f65285f1dd536f1e0 golangci-lint-1.22.2-windows-386.zip
94f04899f620aadc9c1524e5482e415efdbd993fa2b2918c4fec2798f030ac1c golangci-lint-1.22.2-linux-armv7.tar.gz
0e72a87d71edde00b6e37e84a99841833ad55fee83e20d21130a7a622b2860bb golangci-lint-1.22.2-freebsd-386.tar.gz
86def2f31fe8fd7c05674104ed2a4bef3e44b7132b93c6ad2f52f198b3d01801 golangci-lint-1.22.2-linux-s390x.tar.gz
b0df4546d36be94e8107733ba290b98dd9b7e41a42d3fb202e87fc7e4ee800c3 golangci-lint-1.22.2-freebsd-armv6.tar.gz
3d45958dcf6a8d195086d2fced1a21db42a90815dfd156d180efa62dbdda6724 golangci-lint-1.22.2-darwin-386.tar.gz
7ee29f35c74fab017a454237990c74d984ce3855960f2c10509238992bb781f9 golangci-lint-1.22.2-linux-arm64.tar.gz
52086ac52a502b68578e58e35d3964f127c16d7a90b9ffcb399a004d055ded51 golangci-lint-1.22.2-linux-386.tar.gz
c2e4df1fab2ae53762f9baac6041503eeeaa968ce38ea41779f7cb526751c667 golangci-lint-1.22.2-windows-amd64.zip
109d38cdc89f271392f5a138d6782657157f9f496fd4801956efa2d0428e0cbe golangci-lint-1.22.2-linux-amd64.tar.gz
f08aae4868d4828c8f07deb0dcd941a1da695b97e58d15e9f3d1d07dcc7a0c84 golangci-lint-1.22.2-linux-armv6.tar.gz
37af03d9c144d527cb15c46a07e6a22d3f62b5491e34ad6f3bfe6bb0b0b597d4 golangci-lint-1.22.2-linux-ppc64le.tar.gz
251a1081d53944f1d5f86216d752837b23079f90605c9d1cc628da1ffcd2e749 golangci-lint-1.22.2-freebsd-armv7.tar.gz

View File

@@ -58,7 +58,7 @@ import (
"strings"
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/cespare/cp"
"github.com/ethereum/go-ethereum/internal/build"
"github.com/ethereum/go-ethereum/params"
)
@@ -145,6 +145,7 @@ var (
"bionic": "golang-go",
"disco": "golang-go",
"eoan": "golang-go",
"focal": "golang-go",
}
debGoBootPaths = map[string]string{
@@ -214,9 +215,9 @@ func doInstall(cmdline []string) {
var minor int
fmt.Sscanf(strings.TrimPrefix(runtime.Version(), "go1."), "%d", &minor)
if minor < 9 {
if minor < 11 {
log.Println("You have Go version", runtime.Version())
log.Println("go-ethereum requires at least Go version 1.9 and cannot")
log.Println("go-ethereum requires at least Go version 1.11 and cannot")
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
os.Exit(1)
}
@@ -229,18 +230,15 @@ func doInstall(cmdline []string) {
if *arch == "" || *arch == runtime.GOARCH {
goinstall := goTool("install", buildFlags(env)...)
if runtime.GOARCH == "arm64" {
goinstall.Args = append(goinstall.Args, "-p", "1")
}
goinstall.Args = append(goinstall.Args, "-v")
goinstall.Args = append(goinstall.Args, packages...)
build.MustRun(goinstall)
return
}
// If we are cross compiling to ARMv5 ARMv6 or ARMv7, clean any previous builds
if *arch == "arm" {
os.RemoveAll(filepath.Join(runtime.GOROOT(), "pkg", runtime.GOOS+"_arm"))
for _, path := range filepath.SplitList(build.GOPATH()) {
os.RemoveAll(filepath.Join(path, "pkg", runtime.GOOS+"_arm"))
}
}
// Seems we are cross compiling, work around forbidden GOBIN
goinstall := goToolArch(*arch, *cc, "install", buildFlags(env)...)
goinstall.Args = append(goinstall.Args, "-v")
@@ -290,7 +288,6 @@ func goTool(subcmd string, args ...string) *exec.Cmd {
func goToolArch(arch string, cc string, subcmd string, args ...string) *exec.Cmd {
cmd := build.GoTool(subcmd, args...)
cmd.Env = []string{"GOPATH=" + build.GOPATH()}
if arch == "" || arch == runtime.GOARCH {
cmd.Env = append(cmd.Env, "GOBIN="+GOBIN)
} else {
@@ -301,7 +298,7 @@ func goToolArch(arch string, cc string, subcmd string, args ...string) *exec.Cmd
cmd.Env = append(cmd.Env, "CC="+cc)
}
for _, e := range os.Environ() {
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") {
if strings.HasPrefix(e, "GOBIN=") {
continue
}
cmd.Env = append(cmd.Env, e)
@@ -315,6 +312,7 @@ func goToolArch(arch string, cc string, subcmd string, args ...string) *exec.Cmd
func doTest(cmdline []string) {
coverage := flag.Bool("coverage", false, "Whether to record code coverage")
verbose := flag.Bool("v", false, "Whether to log verbosely")
flag.CommandLine.Parse(cmdline)
env := build.Env()
@@ -327,48 +325,50 @@ func doTest(cmdline []string) {
// Test a single package at a time. CI builders are slow
// and some tests run into timeouts under load.
gotest := goTool("test", buildFlags(env)...)
gotest.Args = append(gotest.Args, "-p", "1", "-timeout", "5m", "--short")
gotest.Args = append(gotest.Args, "-p", "1", "-timeout", "5m")
if *coverage {
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
}
if *verbose {
gotest.Args = append(gotest.Args, "-v")
}
gotest.Args = append(gotest.Args, packages...)
build.MustRun(gotest)
}
// runs gometalinter on requested packages
// doLint runs golangci-lint on requested packages.
func doLint(cmdline []string) {
var (
cachedir = flag.String("cachedir", "./build/cache", "directory for caching golangci-lint binary.")
)
flag.CommandLine.Parse(cmdline)
packages := []string{"./..."}
if len(flag.CommandLine.Args()) > 0 {
packages = flag.CommandLine.Args()
}
// Get metalinter and install all supported linters
build.MustRun(goTool("get", "gopkg.in/alecthomas/gometalinter.v2"))
build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v2"), "--install")
// Run fast linters batched together
configs := []string{
"--vendor",
"--tests",
"--deadline=2m",
"--disable-all",
"--enable=goimports",
"--enable=varcheck",
"--enable=vet",
"--enable=gofmt",
"--enable=misspell",
"--enable=goconst",
"--min-occurrences=6", // for goconst
}
build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v2"), append(configs, packages...)...)
linter := downloadLinter(*cachedir)
lflags := []string{"run", "--config", ".golangci.yml"}
build.MustRunCommand(linter, append(lflags, packages...)...)
fmt.Println("You have achieved perfection.")
}
// Run slow linters one by one
for _, linter := range []string{"unconvert", "gosimple"} {
configs = []string{"--vendor", "--tests", "--deadline=10m", "--disable-all", "--enable=" + linter}
build.MustRunCommand(filepath.Join(GOBIN, "gometalinter.v2"), append(configs, packages...)...)
// downloadLinter downloads and unpacks golangci-lint.
func downloadLinter(cachedir string) string {
const version = "1.22.2"
csdb := build.MustLoadChecksums("build/checksums.txt")
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, runtime.GOARCH)
url := fmt.Sprintf("https://github.com/golangci/golangci-lint/releases/download/v%s/%s.tar.gz", version, base)
archivePath := filepath.Join(cachedir, base+".tar.gz")
if err := csdb.DownloadFile(url, archivePath); err != nil {
log.Fatal(err)
}
if err := build.ExtractTarballArchive(archivePath, cachedir); err != nil {
log.Fatal(err)
}
return filepath.Join(cachedir, base, "golangci-lint")
}
// Release Packaging
@@ -472,8 +472,7 @@ func maybeSkipArchive(env build.Environment) {
func doDebianSource(cmdline []string) {
var (
goversion = flag.String("goversion", "", `Go version to build with (will be included in the source package)`)
gobundle = flag.String("gobundle", "/tmp/go.tar.gz", `Filesystem path to cache the downloaded Go bundles at`)
gohash = flag.String("gohash", "", `SHA256 checksum of the Go sources requested to build with`)
cachedir = flag.String("cachedir", "./build/cache", `Filesystem path to cache the downloaded Go bundles at`)
signer = flag.String("signer", "", `Signing key name, also used as package author`)
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")`)
@@ -491,26 +490,40 @@ func doDebianSource(cmdline []string) {
gpg.Stdin = bytes.NewReader(key)
build.MustRun(gpg)
}
// Download and verify the Go source package
if err := build.EnsureGoSources(*goversion, hexutil.MustDecode("0x"+*gohash), *gobundle); err != nil {
log.Fatalf("Failed to ensure Go source package: %v", err)
}
// Create Debian packages and upload them
// Download and verify the Go source package.
gobundle := downloadGoSources(*goversion, *cachedir)
// Download all the dependencies needed to build the sources and run the ci script
srcdepfetch := goTool("install", "-n", "./...")
srcdepfetch.Env = append(os.Environ(), "GOPATH="+filepath.Join(*workdir, "modgopath"))
build.MustRun(srcdepfetch)
cidepfetch := goTool("run", "./build/ci.go")
cidepfetch.Env = append(os.Environ(), "GOPATH="+filepath.Join(*workdir, "modgopath"))
cidepfetch.Run() // Command fails, don't care, we only need the deps to start it
// Create Debian packages and upload them.
for _, pkg := range debPackages {
for distro, goboot := range debDistroGoBoots {
// Prepare the debian package with the go-ethereum sources
// Prepare the debian package with the go-ethereum sources.
meta := newDebMetadata(distro, goboot, *signer, env, now, pkg.Name, pkg.Version, pkg.Executables)
pkgdir := stageDebianSource(*workdir, meta)
// Ship the Go sources along so we have a proper thing to build with
if err := build.ExtractTarballArchive(*gobundle, pkgdir); err != nil {
// Add Go source code
if err := build.ExtractTarballArchive(gobundle, pkgdir); err != nil {
log.Fatalf("Failed to extract Go sources: %v", err)
}
if err := os.Rename(filepath.Join(pkgdir, "go"), filepath.Join(pkgdir, ".go")); err != nil {
log.Fatalf("Failed to rename Go source folder: %v", err)
}
// Add all dependency modules in compressed form
os.MkdirAll(filepath.Join(pkgdir, ".mod", "cache"), 0755)
if err := cp.CopyAll(filepath.Join(pkgdir, ".mod", "cache", "download"), filepath.Join(*workdir, "modgopath", "pkg", "mod", "cache", "download")); err != nil {
log.Fatalf("Failed to copy Go module dependencies: %v", err)
}
// Run the packaging and upload to the PPA
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc", "-d", "-Zxz")
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc", "-d", "-Zxz", "-nc")
debuild.Dir = pkgdir
build.MustRun(debuild)
@@ -530,6 +543,17 @@ func doDebianSource(cmdline []string) {
}
}
func downloadGoSources(version string, cachedir string) string {
csdb := build.MustLoadChecksums("build/checksums.txt")
file := fmt.Sprintf("go%s.src.tar.gz", version)
url := "https://dl.google.com/go/" + file
dst := filepath.Join(cachedir, file)
if err := csdb.DownloadFile(url, dst); err != nil {
log.Fatal(err)
}
return dst
}
func ppaUpload(workdir, ppa, sshUser string, files []string) {
p := strings.Split(ppa, "/")
if len(p) != 2 {
@@ -763,9 +787,12 @@ func doWindowsInstaller(cmdline []string) {
build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools)
build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil)
build.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll", 0755)
build.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING", 0755)
if err := cp.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll"); err != nil {
log.Fatal("Failed to copy SimpleFC.dll: %v", err)
}
if err := cp.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING"); err != nil {
log.Fatal("Failed to copy copyright note: %v", err)
}
// Build the installer. This assumes that all the needed files have been previously
// built (don't mix building and packaging to keep cross compilation complexity to a
// minimum).
@@ -782,7 +809,6 @@ func doWindowsInstaller(cmdline []string) {
"/DARCH="+*arch,
filepath.Join(*workdir, "geth.nsi"),
)
// Sign and publish installer.
if err := archiveUpload(installer, *upload, *signer); err != nil {
log.Fatal(err)
@@ -855,7 +881,6 @@ func gomobileTool(subcmd string, args ...string) *exec.Cmd {
cmd := exec.Command(filepath.Join(GOBIN, "gomobile"), subcmd)
cmd.Args = append(cmd.Args, args...)
cmd.Env = []string{
"GOPATH=" + build.GOPATH(),
"PATH=" + GOBIN + string(os.PathListSeparator) + os.Getenv("PATH"),
}
for _, e := range os.Environ() {
@@ -1043,16 +1068,10 @@ func doXgo(cmdline []string) {
func xgoTool(args []string) *exec.Cmd {
cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
cmd.Env = []string{
"GOPATH=" + build.GOPATH(),
cmd.Env = os.Environ()
cmd.Env = append(cmd.Env, []string{
"GOBIN=" + GOBIN,
}
for _, e := range os.Environ() {
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") {
continue
}
cmd.Env = append(cmd.Env, e)
}
}...)
return cmd
}

View File

@@ -1,19 +0,0 @@
#!/bin/sh
# Cleaning the Go cache only makes sense if we actually have Go installed... or
# if Go is actually callable. This does not hold true during deb packaging, so
# we need an explicit check to avoid build failures.
if ! command -v go > /dev/null; then
exit
fi
version_gt() {
test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"
}
golang_version=$(go version |cut -d' ' -f3 |sed 's/go//')
# Clean go build cache when go version is greater than or equal to 1.10
if !(version_gt 1.10 $golang_version); then
go clean -cache
fi

View File

@@ -4,13 +4,27 @@
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
# Launchpad rejects Go's access to $HOME/.cache, use custom folder
# Launchpad rejects Go's access to $HOME, use custom folders
export GOCACHE=/tmp/go-build
export GOPATH=/tmp/gopath
export GOROOT_BOOTSTRAP={{.GoBootPath}}
override_dh_auto_clean:
# Don't try to be smart Launchpad, we know our build rules better than you
override_dh_auto_build:
(cd .go/src && ./make.bash)
build/env.sh .go/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
# We can't download a fresh Go within Launchpad, so we're shipping and building
# one on the fly. However, we can't build it inside the go-ethereum folder as
# bootstrapping clashes with go modules, so build in a sibling folder.
(mv .go ../ && cd ../.go/src && ./make.bash)
# We can't download external go modules within Launchpad, so we're shipping the
# entire dependency source cache with go-ethereum.
mkdir -p $(GOPATH)/pkg
mv .mod $(GOPATH)/pkg/mod
# A fresh Go was built, all dependency downloads faked, hope build works now
../.go/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
override_dh_auto_test:

View File

@@ -1,30 +0,0 @@
#!/bin/sh
set -e
if [ ! -f "build/env.sh" ]; then
echo "$0 must be run from the root of the repository."
exit 2
fi
# Create fake Go workspace if it doesn't exist yet.
workspace="$PWD/build/_workspace"
root="$PWD"
ethdir="$workspace/src/github.com/ethereum"
if [ ! -L "$ethdir/go-ethereum" ]; then
mkdir -p "$ethdir"
cd "$ethdir"
ln -s ../../../../../. go-ethereum
cd "$root"
fi
# Set up the environment to use the workspace.
GOPATH="$workspace"
export GOPATH
# Run the command inside the workspace.
cd "$ethdir/go-ethereum"
PWD="$ethdir/go-ethereum"
# Launch the arguments with the configured environment.
exec "$@"

74
cmd/abidump/main.go Normal file
View File

@@ -0,0 +1,74 @@
// 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 main
import (
"encoding/hex"
"flag"
"fmt"
"os"
"strings"
"github.com/ethereum/go-ethereum/signer/core"
"github.com/ethereum/go-ethereum/signer/fourbyte"
)
func init() {
flag.Usage = func() {
fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "<hexdata>")
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, `
Parses the given ABI data and tries to interpret it from the fourbyte database.`)
}
}
func parse(data []byte) {
db, err := fourbyte.New()
if err != nil {
die(err)
}
messages := core.ValidationMessages{}
db.ValidateCallData(nil, data, &messages)
for _, m := range messages.Messages {
fmt.Printf("%v: %v\n", m.Typ, m.Message)
}
}
// Example
// ./abidump a9059cbb000000000000000000000000ea0e2dc7d65a50e77fc7e84bff3fd2a9e781ff5c0000000000000000000000000000000000000000000000015af1d78b58c40000
func main() {
flag.Parse()
switch {
case flag.NArg() == 1:
hexdata := flag.Arg(0)
data, err := hex.DecodeString(strings.TrimPrefix(hexdata, "0x"))
if err != nil {
die(err)
}
parse(data)
default:
fmt.Fprintln(os.Stderr, "Error: one argument needed")
flag.Usage()
os.Exit(2)
}
}
func die(args ...interface{}) {
fmt.Fprintln(os.Stderr, args...)
os.Exit(1)
}

View File

@@ -21,8 +21,11 @@ import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common/compiler"
@@ -31,19 +34,6 @@ import (
"gopkg.in/urfave/cli.v1"
)
const (
commandHelperTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .Flags}}
OPTIONS:
{{range $.Flags}}{{"\t"}}{{.}}
{{end}}
{{end}}`
)
var (
// Git SHA1 commit hash of the release (set via linker flags)
gitCommit = ""
@@ -103,6 +93,10 @@ var (
Usage: "Destination language for the bindings (go, java, objc)",
Value: "go",
}
aliasFlag = cli.StringFlag{
Name: "alias",
Usage: "Comma separated aliases for function and event renaming, e.g. foo=bar",
}
)
func init() {
@@ -120,9 +114,10 @@ func init() {
pkgFlag,
outFlag,
langFlag,
aliasFlag,
}
app.Action = utils.MigrateFlags(abigen)
cli.CommandHelpTemplate = commandHelperTemplate
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
}
func abigen(c *cli.Context) error {
@@ -144,11 +139,12 @@ func abigen(c *cli.Context) error {
}
// If the entire solidity code was specified, build and bind based on that
var (
abis []string
bins []string
types []string
sigs []map[string]string
libs = make(map[string]string)
abis []string
bins []string
types []string
sigs []map[string]string
libs = make(map[string]string)
aliases = make(map[string]string)
)
if c.GlobalString(abiFlag.Name) != "" {
// Load up the ABI, optional bytecode and type name from the parameters
@@ -199,10 +195,22 @@ func abigen(c *cli.Context) error {
utils.Fatalf("Failed to build Solidity contract: %v", err)
}
case c.GlobalIsSet(vyFlag.Name):
contracts, err = compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
output, err := compiler.CompileVyper(c.GlobalString(vyperFlag.Name), c.GlobalString(vyFlag.Name))
if err != nil {
utils.Fatalf("Failed to build Vyper contract: %v", err)
}
contracts = make(map[string]*compiler.Contract)
for n, contract := range output {
name := n
// Sanitize the combined json names to match the
// format expected by solidity.
if !strings.Contains(n, ":") {
// Remove extra path components
name = abi.ToCamelCase(strings.TrimSuffix(filepath.Base(name), ".vy"))
}
contracts[name] = contract
}
case c.GlobalIsSet(jsonFlag.Name):
jsonOutput, err := ioutil.ReadFile(c.GlobalString(jsonFlag.Name))
if err != nil {
@@ -232,8 +240,20 @@ func abigen(c *cli.Context) error {
libs[libPattern] = nameParts[len(nameParts)-1]
}
}
// Extract all aliases from the flags
if c.GlobalIsSet(aliasFlag.Name) {
// We support multi-versions for aliasing
// e.g.
// foo=bar,foo2=bar2
// foo:bar,foo2:bar2
re := regexp.MustCompile(`(?:(\w+)[:=](\w+))`)
submatches := re.FindAllStringSubmatch(c.GlobalString(aliasFlag.Name), -1)
for _, match := range submatches {
aliases[match[1]] = match[2]
}
}
// Generate the contract binding
code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs)
code, err := bind.Bind(types, abis, bins, sigs, c.GlobalString(pkgFlag.Name), lang, libs, aliases)
if err != nil {
utils.Fatalf("Failed to generate ABI binding: %v", err)
}

View File

@@ -28,19 +28,6 @@ import (
"gopkg.in/urfave/cli.v1"
)
const (
commandHelperTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .Flags}}
OPTIONS:
{{range $.Flags}}{{"\t"}}{{.}}
{{end}}
{{end}}`
)
var (
// Git SHA1 commit hash of the release (set via linker flags)
gitCommit = ""
@@ -61,7 +48,7 @@ func init() {
oracleFlag,
nodeURLFlag,
}
cli.CommandHelpTemplate = commandHelperTemplate
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
}
// Commonly used command line flags.

View File

@@ -223,6 +223,7 @@ func init() {
}
app.Action = signer
app.Commands = []cli.Command{initCommand, attestCommand, setCredentialCommand, delCredentialCommand, gendocCommand}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
}
func main() {
@@ -387,7 +388,7 @@ func initialize(c *cli.Context) error {
if c.GlobalBool(stdiouiFlag.Name) {
logOutput = os.Stderr
// If using the stdioui, we can't do the 'confirm'-flow
fmt.Fprintf(logOutput, legalWarning)
fmt.Fprint(logOutput, legalWarning)
} else {
if !confirm(legalWarning) {
return fmt.Errorf("aborted by user")
@@ -579,7 +580,7 @@ func signer(c *cli.Context) error {
},
})
abortChan := make(chan os.Signal)
abortChan := make(chan os.Signal, 1)
signal.Notify(abortChan, os.Interrupt)
sig := <-abortChan
@@ -694,7 +695,7 @@ func checkFile(filename string) error {
// confirm displays a text and asks for user confirmation
func confirm(text string) bool {
fmt.Printf(text)
fmt.Print(text)
fmt.Printf("\nEnter 'ok' to proceed:\n> ")
text, err := bufio.NewReader(os.Stdin).ReadString('\n')
@@ -760,21 +761,19 @@ func testExternalUI(api *core.SignerAPI) {
api.UI.ShowInfo("Please approve the next request for signing a clique header")
time.Sleep(delay)
cliqueHeader := types.Header{
common.HexToHash("0000H45H"),
common.HexToHash("0000H45H"),
common.HexToAddress("0000H45H"),
common.HexToHash("0000H00H"),
common.HexToHash("0000H45H"),
common.HexToHash("0000H45H"),
types.Bloom{},
big.NewInt(1337),
big.NewInt(1337),
1338,
1338,
1338,
[]byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"),
common.HexToHash("0x0000H45H"),
types.BlockNonce{},
ParentHash: common.HexToHash("0000H45H"),
UncleHash: common.HexToHash("0000H45H"),
Coinbase: common.HexToAddress("0000H45H"),
Root: common.HexToHash("0000H00H"),
TxHash: common.HexToHash("0000H45H"),
ReceiptHash: common.HexToHash("0000H45H"),
Difficulty: big.NewInt(1337),
Number: big.NewInt(1337),
GasLimit: 1338,
GasUsed: 1338,
Time: 1338,
Extra: []byte("Extra data Extra data Extra data Extra data Extra data Extra data Extra data Extra data"),
MixDigest: common.HexToHash("0x0000H45H"),
}
cliqueRlp, err := rlp.EncodeToBytes(cliqueHeader)
if err != nil {
@@ -938,7 +937,7 @@ func GenDoc(ctx *cli.Context) {
"of the work in canonicalizing and making sense of the data, and it's up to the UI to present" +
"the user with the contents of the `message`"
sighash, msg := accounts.TextAndHash([]byte("hello world"))
messages := []*core.NameValueType{{"message", msg, accounts.MimetypeTextPlain}}
messages := []*core.NameValueType{{Name: "message", Value: msg, Typ: accounts.MimetypeTextPlain}}
add("SignDataRequest", desc, &core.SignDataRequest{
Address: common.NewMixedcaseAddress(a),
@@ -969,8 +968,8 @@ func GenDoc(ctx *cli.Context) {
add("SignTxRequest", desc, &core.SignTxRequest{
Meta: meta,
Callinfo: []core.ValidationInfo{
{"Warning", "Something looks odd, show this message as a warning"},
{"Info", "User should see this aswell"},
{Typ: "Warning", Message: "Something looks odd, show this message as a warning"},
{Typ: "Info", Message: "User should see this as well"},
},
Transaction: core.SendTxArgs{
Data: &data,
@@ -1036,16 +1035,21 @@ func GenDoc(ctx *cli.Context) {
&core.ListRequest{
Meta: meta,
Accounts: []accounts.Account{
{a, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/a"}},
{b, accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
{Address: a, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/a"}},
{Address: b, URL: accounts.URL{Scheme: "keystore", Path: "/path/to/keyfile/b"}}},
})
add("ListResponse", "Response to list request. The response contains a list of all addresses to show to the caller. "+
"Note: the UI is free to respond with any address the caller, regardless of whether it exists or not",
&core.ListResponse{
Accounts: []accounts.Account{
{common.HexToAddress("0xcowbeef000000cowbeef00000000000000000c0w"), accounts.URL{Path: ".. ignored .."}},
{common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff"), accounts.URL{}},
{
Address: common.HexToAddress("0xcowbeef000000cowbeef00000000000000000c0w"),
URL: accounts.URL{Path: ".. ignored .."},
},
{
Address: common.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff"),
},
}})
}

View File

@@ -1,6 +1,6 @@
## Initializing Clef
First thing's first, Clef needs to store some data itself. Since that data might be sensitive (passwords, signing rules, accounts), Clef's entire storage is encrypted. To support encrypting data, the first step is to initialize Clef with a random master seed, itself too encrypted with your chosen password:
First things first, Clef needs to store some data itself. Since that data might be sensitive (passwords, signing rules, accounts), Clef's entire storage is encrypted. To support encrypting data, the first step is to initialize Clef with a random master seed, itself too encrypted with your chosen password:
```text
$ clef init

View File

@@ -130,9 +130,9 @@ func (c *cloudflareClient) uploadRecords(name string, records map[string]string)
if !exists {
// Entry is unknown, push a new one to Cloudflare.
log.Info(fmt.Sprintf("Creating %s = %q", path, val))
ttl := 1
ttl := rootTTL
if path != name {
ttl = 2147483647 // Max TTL permitted by Cloudflare
ttl = treeNodeTTL // Max TTL permitted by Cloudflare
}
_, err = c.CreateDNSRecord(c.zoneID, cloudflare.DNSRecord{Type: "TXT", Name: path, Content: val, TTL: ttl})
} else if old.Content != val {

303
cmd/devp2p/dns_route53.go Normal file
View File

@@ -0,0 +1,303 @@
// 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 (
"errors"
"fmt"
"sort"
"strconv"
"strings"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/credentials"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/route53"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/dnsdisc"
"gopkg.in/urfave/cli.v1"
)
// The Route53 limits change sets to this size. DNS changes need to be split
// up into multiple batches to work around the limit.
const route53ChangeLimit = 30000
var (
route53AccessKeyFlag = cli.StringFlag{
Name: "access-key-id",
Usage: "AWS Access Key ID",
EnvVar: "AWS_ACCESS_KEY_ID",
}
route53AccessSecretFlag = cli.StringFlag{
Name: "access-key-secret",
Usage: "AWS Access Key Secret",
EnvVar: "AWS_SECRET_ACCESS_KEY",
}
route53ZoneIDFlag = cli.StringFlag{
Name: "zone-id",
Usage: "Route53 Zone ID",
}
)
type route53Client struct {
api *route53.Route53
zoneID string
}
type recordSet struct {
values []string
ttl int64
}
// newRoute53Client sets up a Route53 API client from command line flags.
func newRoute53Client(ctx *cli.Context) *route53Client {
akey := ctx.String(route53AccessKeyFlag.Name)
asec := ctx.String(route53AccessSecretFlag.Name)
if akey == "" || asec == "" {
exit(fmt.Errorf("need Route53 Access Key ID and secret proceed"))
}
config := &aws.Config{Credentials: credentials.NewStaticCredentials(akey, asec, "")}
session, err := session.NewSession(config)
if err != nil {
exit(fmt.Errorf("can't create AWS session: %v", err))
}
return &route53Client{
api: route53.New(session),
zoneID: ctx.String(route53ZoneIDFlag.Name),
}
}
// deploy uploads the given tree to Route53.
func (c *route53Client) deploy(name string, t *dnsdisc.Tree) error {
if err := c.checkZone(name); err != nil {
return err
}
// Compute DNS changes.
existing, err := c.collectRecords(name)
if err != nil {
return err
}
log.Info(fmt.Sprintf("Found %d TXT records", len(existing)))
records := t.ToTXT(name)
changes := c.computeChanges(name, records, existing)
if len(changes) == 0 {
log.Info("No DNS changes needed")
return nil
}
// Submit change batches.
batches := splitChanges(changes, route53ChangeLimit)
for i, changes := range batches {
log.Info(fmt.Sprintf("Submitting %d changes to Route53", len(changes)))
batch := new(route53.ChangeBatch)
batch.SetChanges(changes)
batch.SetComment(fmt.Sprintf("enrtree update %d/%d of %s at seq %d", i+1, len(batches), name, t.Seq()))
req := &route53.ChangeResourceRecordSetsInput{HostedZoneId: &c.zoneID, ChangeBatch: batch}
resp, err := c.api.ChangeResourceRecordSets(req)
if err != nil {
return err
}
log.Info(fmt.Sprintf("Waiting for change request %s", *resp.ChangeInfo.Id))
wreq := &route53.GetChangeInput{Id: resp.ChangeInfo.Id}
if err := c.api.WaitUntilResourceRecordSetsChanged(wreq); err != nil {
return err
}
}
return nil
}
// checkZone verifies zone information for the given domain.
func (c *route53Client) checkZone(name string) (err error) {
if c.zoneID == "" {
c.zoneID, err = c.findZoneID(name)
}
return err
}
// findZoneID searches for the Zone ID containing the given domain.
func (c *route53Client) findZoneID(name string) (string, error) {
log.Info(fmt.Sprintf("Finding Route53 Zone ID for %s", name))
var req route53.ListHostedZonesByNameInput
for {
resp, err := c.api.ListHostedZonesByName(&req)
if err != nil {
return "", err
}
for _, zone := range resp.HostedZones {
if isSubdomain(name, *zone.Name) {
return *zone.Id, nil
}
}
if !*resp.IsTruncated {
break
}
req.DNSName = resp.NextDNSName
req.HostedZoneId = resp.NextHostedZoneId
}
return "", errors.New("can't find zone ID for " + name)
}
// computeChanges creates DNS changes for the given record.
func (c *route53Client) computeChanges(name string, records map[string]string, existing map[string]recordSet) []*route53.Change {
// Convert all names to lowercase.
lrecords := make(map[string]string, len(records))
for name, r := range records {
lrecords[strings.ToLower(name)] = r
}
records = lrecords
var changes []*route53.Change
for path, val := range records {
ttl := int64(rootTTL)
if path != name {
ttl = int64(treeNodeTTL)
}
prevRecords, exists := existing[path]
prevValue := strings.Join(prevRecords.values, "")
if !exists {
// Entry is unknown, push a new one
log.Info(fmt.Sprintf("Creating %s = %q", path, val))
changes = append(changes, newTXTChange("CREATE", path, ttl, splitTXT(val)))
} else if prevValue != val {
// Entry already exists, only change its content.
log.Info(fmt.Sprintf("Updating %s from %q to %q", path, prevValue, val))
changes = append(changes, newTXTChange("UPSERT", path, ttl, splitTXT(val)))
} else {
log.Info(fmt.Sprintf("Skipping %s = %q", path, val))
}
}
// Iterate over the old records and delete anything stale.
for path, set := range existing {
if _, ok := records[path]; ok {
continue
}
// Stale entry, nuke it.
log.Info(fmt.Sprintf("Deleting %s = %q", path, strings.Join(set.values, "")))
changes = append(changes, newTXTChange("DELETE", path, set.ttl, set.values...))
}
sortChanges(changes)
return changes
}
// sortChanges ensures DNS changes are in leaf-added -> root-changed -> leaf-deleted order.
func sortChanges(changes []*route53.Change) {
score := map[string]int{"CREATE": 1, "UPSERT": 2, "DELETE": 3}
sort.Slice(changes, func(i, j int) bool {
if *changes[i].Action == *changes[j].Action {
return *changes[i].ResourceRecordSet.Name < *changes[j].ResourceRecordSet.Name
}
return score[*changes[i].Action] < score[*changes[j].Action]
})
}
// splitChanges splits up DNS changes such that each change batch
// is smaller than the given RDATA limit.
func splitChanges(changes []*route53.Change, limit int) [][]*route53.Change {
var batches [][]*route53.Change
var batchSize int
for _, ch := range changes {
// Start new batch if this change pushes the current one over the limit.
size := changeSize(ch)
if len(batches) == 0 || batchSize+size > limit {
batches = append(batches, nil)
batchSize = 0
}
batches[len(batches)-1] = append(batches[len(batches)-1], ch)
batchSize += size
}
return batches
}
// changeSize returns the RDATA size of a DNS change.
func changeSize(ch *route53.Change) int {
size := 0
for _, rr := range ch.ResourceRecordSet.ResourceRecords {
if rr.Value != nil {
size += len(*rr.Value)
}
}
return size
}
// collectRecords collects all TXT records below the given name.
func (c *route53Client) collectRecords(name string) (map[string]recordSet, error) {
log.Info(fmt.Sprintf("Retrieving existing TXT records on %s (%s)", name, c.zoneID))
var req route53.ListResourceRecordSetsInput
req.SetHostedZoneId(c.zoneID)
existing := make(map[string]recordSet)
err := c.api.ListResourceRecordSetsPages(&req, func(resp *route53.ListResourceRecordSetsOutput, last bool) bool {
for _, set := range resp.ResourceRecordSets {
if !isSubdomain(*set.Name, name) || *set.Type != "TXT" {
continue
}
s := recordSet{ttl: *set.TTL}
for _, rec := range set.ResourceRecords {
s.values = append(s.values, *rec.Value)
}
name := strings.TrimSuffix(*set.Name, ".")
existing[name] = s
}
return true
})
return existing, err
}
// newTXTChange creates a change to a TXT record.
func newTXTChange(action, name string, ttl int64, values ...string) *route53.Change {
var c route53.Change
var r route53.ResourceRecordSet
var rrs []*route53.ResourceRecord
for _, val := range values {
rr := new(route53.ResourceRecord)
rr.SetValue(val)
rrs = append(rrs, rr)
}
r.SetType("TXT")
r.SetName(name)
r.SetTTL(ttl)
r.SetResourceRecords(rrs)
c.SetAction(action)
c.SetResourceRecordSet(&r)
return &c
}
// isSubdomain returns true if name is a subdomain of domain.
func isSubdomain(name, domain string) bool {
domain = strings.TrimSuffix(domain, ".")
name = strings.TrimSuffix(name, ".")
return strings.HasSuffix("."+name, "."+domain)
}
// splitTXT splits value into a list of quoted 255-character strings.
func splitTXT(value string) string {
var result strings.Builder
for len(value) > 0 {
rlen := len(value)
if rlen > 253 {
rlen = 253
}
result.WriteString(strconv.Quote(value[:rlen]))
value = value[rlen:]
}
return result.String()
}

View File

@@ -0,0 +1,154 @@
// Copyright 2020 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 (
"reflect"
"testing"
"github.com/aws/aws-sdk-go/service/route53"
)
// This test checks that computeChanges/splitChanges create DNS changes in
// leaf-added -> root-changed -> leaf-deleted order.
func TestRoute53ChangeSort(t *testing.T) {
testTree0 := map[string]recordSet{
"2kfjogvxdqtxxugbh7gs7naaai.n": {ttl: 3333, values: []string{
`"enr:-HW4QO1ml1DdXLeZLsUxewnthhUy8eROqkDyoMTyavfks9JlYQIlMFEUoM78PovJDPQrAkrb3LRJ-""vtrymDguKCOIAWAgmlkgnY0iXNlY3AyNTZrMaEDffaGfJzgGhUif1JqFruZlYmA31HzathLSWxfbq_QoQ4"`,
}},
"fdxn3sn67na5dka4j2gok7bvqi.n": {ttl: treeNodeTTL, values: []string{`"enrtree-branch:"`}},
"n": {ttl: rootTTL, values: []string{`"enrtree-root:v1 e=2KFJOGVXDQTXXUGBH7GS7NAAAI l=FDXN3SN67NA5DKA4J2GOK7BVQI seq=0 sig=v_-J_q_9ICQg5ztExFvLQhDBGMb0lZPJLhe3ts9LAcgqhOhtT3YFJsl8BWNDSwGtamUdR-9xl88_w-X42SVpjwE"`}},
}
testTree1 := map[string]string{
"n": "enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA",
"C7HRFPF3BLGF3YR4DY5KX3SMBE.n": "enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org",
"JWXYDBPXYWG6FX3GMDIBFA6CJ4.n": "enrtree-branch:2XS2367YHAXJFGLZHVAWLQD4ZY,H4FHT4B454P6UXFD7JCYQ5PWDY,MHTDO6TMUBRIA2XWG5LUDACK24",
"2XS2367YHAXJFGLZHVAWLQD4ZY.n": "enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA",
"H4FHT4B454P6UXFD7JCYQ5PWDY.n": "enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI",
"MHTDO6TMUBRIA2XWG5LUDACK24.n": "enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o",
}
wantChanges := []*route53.Change{
{
Action: sp("CREATE"),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: sp("2xs2367yhaxjfglzhvawlqd4zy.n"),
ResourceRecords: []*route53.ResourceRecord{{
Value: sp(`"enr:-HW4QOFzoVLaFJnNhbgMoDXPnOvcdVuj7pDpqRvh6BRDO68aVi5ZcjB3vzQRZH2IcLBGHzo8uUN3snqmgTiE56CH3AMBgmlkgnY0iXNlY3AyNTZrMaECC2_24YYkYHEgdzxlSNKQEnHhuNAbNlMlWJxrJxbAFvA"`),
}},
TTL: ip(treeNodeTTL),
Type: sp("TXT"),
},
},
{
Action: sp("CREATE"),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: sp("c7hrfpf3blgf3yr4dy5kx3smbe.n"),
ResourceRecords: []*route53.ResourceRecord{{
Value: sp(`"enrtree://AM5FCQLWIZX2QFPNJAP7VUERCCRNGRHWZG3YYHIUV7BVDQ5FDPRT2@morenodes.example.org"`),
}},
TTL: ip(treeNodeTTL),
Type: sp("TXT"),
},
},
{
Action: sp("CREATE"),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: sp("h4fht4b454p6uxfd7jcyq5pwdy.n"),
ResourceRecords: []*route53.ResourceRecord{{
Value: sp(`"enr:-HW4QAggRauloj2SDLtIHN1XBkvhFZ1vtf1raYQp9TBW2RD5EEawDzbtSmlXUfnaHcvwOizhVYLtr7e6vw7NAf6mTuoCgmlkgnY0iXNlY3AyNTZrMaECjrXI8TLNXU0f8cthpAMxEshUyQlK-AM0PW2wfrnacNI"`),
}},
TTL: ip(treeNodeTTL),
Type: sp("TXT"),
},
},
{
Action: sp("CREATE"),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: sp("jwxydbpxywg6fx3gmdibfa6cj4.n"),
ResourceRecords: []*route53.ResourceRecord{{
Value: sp(`"enrtree-branch:2XS2367YHAXJFGLZHVAWLQD4ZY,H4FHT4B454P6UXFD7JCYQ5PWDY,MHTDO6TMUBRIA2XWG5LUDACK24"`),
}},
TTL: ip(treeNodeTTL),
Type: sp("TXT"),
},
},
{
Action: sp("CREATE"),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: sp("mhtdo6tmubria2xwg5ludack24.n"),
ResourceRecords: []*route53.ResourceRecord{{
Value: sp(`"enr:-HW4QLAYqmrwllBEnzWWs7I5Ev2IAs7x_dZlbYdRdMUx5EyKHDXp7AV5CkuPGUPdvbv1_Ms1CPfhcGCvSElSosZmyoqAgmlkgnY0iXNlY3AyNTZrMaECriawHKWdDRk2xeZkrOXBQ0dfMFLHY4eENZwdufn1S1o"`),
}},
TTL: ip(treeNodeTTL),
Type: sp("TXT"),
},
},
{
Action: sp("UPSERT"),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: sp("n"),
ResourceRecords: []*route53.ResourceRecord{{
Value: sp(`"enrtree-root:v1 e=JWXYDBPXYWG6FX3GMDIBFA6CJ4 l=C7HRFPF3BLGF3YR4DY5KX3SMBE seq=1 sig=o908WmNp7LibOfPsr4btQwatZJ5URBr2ZAuxvK4UWHlsB9sUOTJQaGAlLPVAhM__XJesCHxLISo94z5Z2a463gA"`),
}},
TTL: ip(rootTTL),
Type: sp("TXT"),
},
},
{
Action: sp("DELETE"),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: sp("2kfjogvxdqtxxugbh7gs7naaai.n"),
ResourceRecords: []*route53.ResourceRecord{
{Value: sp(`"enr:-HW4QO1ml1DdXLeZLsUxewnthhUy8eROqkDyoMTyavfks9JlYQIlMFEUoM78PovJDPQrAkrb3LRJ-""vtrymDguKCOIAWAgmlkgnY0iXNlY3AyNTZrMaEDffaGfJzgGhUif1JqFruZlYmA31HzathLSWxfbq_QoQ4"`)},
},
TTL: ip(3333),
Type: sp("TXT"),
},
},
{
Action: sp("DELETE"),
ResourceRecordSet: &route53.ResourceRecordSet{
Name: sp("fdxn3sn67na5dka4j2gok7bvqi.n"),
ResourceRecords: []*route53.ResourceRecord{{
Value: sp(`"enrtree-branch:"`),
}},
TTL: ip(treeNodeTTL),
Type: sp("TXT"),
},
},
}
var client route53Client
changes := client.computeChanges("n", testTree1, testTree0)
if !reflect.DeepEqual(changes, wantChanges) {
t.Fatalf("wrong changes (got %d, want %d)", len(changes), len(wantChanges))
}
wantSplit := [][]*route53.Change{
wantChanges[:4],
wantChanges[4:8],
}
split := splitChanges(changes, 600)
if !reflect.DeepEqual(split, wantSplit) {
t.Fatalf("wrong split batches: got %d, want %d", len(split), len(wantSplit))
}
}
func sp(s string) *string { return &s }
func ip(i int64) *int64 { return &i }

View File

@@ -42,6 +42,7 @@ var (
dnsSignCommand,
dnsTXTCommand,
dnsCloudflareCommand,
dnsRoute53Command,
},
}
dnsSyncCommand = cli.Command{
@@ -66,11 +67,18 @@ var (
}
dnsCloudflareCommand = cli.Command{
Name: "to-cloudflare",
Usage: "Deploy DNS TXT records to cloudflare",
Usage: "Deploy DNS TXT records to CloudFlare",
ArgsUsage: "<tree-directory>",
Action: dnsToCloudflare,
Flags: []cli.Flag{cloudflareTokenFlag, cloudflareZoneIDFlag},
}
dnsRoute53Command = cli.Command{
Name: "to-route53",
Usage: "Deploy DNS TXT records to Amazon Route53",
ArgsUsage: "<tree-directory>",
Action: dnsToRoute53,
Flags: []cli.Flag{route53AccessKeyFlag, route53AccessSecretFlag, route53ZoneIDFlag},
}
)
var (
@@ -88,6 +96,11 @@ var (
}
)
const (
rootTTL = 1
treeNodeTTL = 2147483647
)
// dnsSync performs dnsSyncCommand.
func dnsSync(ctx *cli.Context) error {
var (
@@ -194,6 +207,19 @@ func dnsToCloudflare(ctx *cli.Context) error {
return client.deploy(domain, t)
}
// dnsToRoute53 peforms dnsRoute53Command.
func dnsToRoute53(ctx *cli.Context) error {
if ctx.NArg() < 1 {
return fmt.Errorf("need tree definition directory as argument")
}
domain, t, err := loadTreeDefinitionForExport(ctx.Args().Get(0))
if err != nil {
return err
}
client := newRoute53Client(ctx)
return client.deploy(domain, t)
}
// loadSigningKey loads a private key in Ethereum keystore format.
func loadSigningKey(keyfile string) *ecdsa.PrivateKey {
keyjson, err := ioutil.ReadFile(keyfile)
@@ -214,8 +240,7 @@ func dnsClient(ctx *cli.Context) *dnsdisc.Client {
if commandHasFlag(ctx, dnsTimeoutFlag) {
cfg.Timeout = ctx.Duration(dnsTimeoutFlag.Name)
}
c, _ := dnsdisc.NewClient(cfg) // cannot fail because no URLs given
return c
return dnsdisc.NewClient(cfg)
}
// There are two file formats for DNS node trees on disk:

View File

@@ -45,7 +45,7 @@ func init() {
// Set up the CLI app.
app.Flags = append(app.Flags, debug.Flags...)
app.Before = func(ctx *cli.Context) error {
return debug.Setup(ctx, "")
return debug.Setup(ctx)
}
app.After = func(ctx *cli.Context) error {
debug.Exit()

View File

@@ -77,7 +77,7 @@ Change the password of a keyfile.`,
}
// Then write the new keyfile in place of the old one.
if err := ioutil.WriteFile(keyfilepath, newJson, 600); err != nil {
if err := ioutil.WriteFile(keyfilepath, newJson, 0600); err != nil {
utils.Fatalf("Error writing new keyfile to disk: %v", err)
}

View File

@@ -52,6 +52,10 @@ If you want to encrypt an existing private key, it can be specified by setting
Name: "privatekey",
Usage: "file containing a raw private key to encrypt",
},
cli.BoolFlag{
Name: "lightkdf",
Usage: "use less secure scrypt parameters",
},
},
Action: func(ctx *cli.Context) error {
// Check if keyfile path given and make sure it doesn't already exist.
@@ -91,7 +95,11 @@ If you want to encrypt an existing private key, it can be specified by setting
// Encrypt key with passphrase.
passphrase := promptPassphrase(true)
keyjson, err := keystore.EncryptKey(key, passphrase, keystore.StandardScryptN, keystore.StandardScryptP)
scryptN, scryptP := keystore.StandardScryptN, keystore.StandardScryptP
if ctx.Bool("lightkdf") {
scryptN, scryptP = keystore.LightScryptN, keystore.LightScryptP
}
keyjson, err := keystore.EncryptKey(key, passphrase, scryptN, scryptP)
if err != nil {
utils.Fatalf("Error encrypting key: %v", err)
}

View File

@@ -43,6 +43,7 @@ func init() {
commandSignMessage,
commandVerifyMessage,
}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
}
// Commonly used command line flags.

View File

@@ -34,7 +34,7 @@ func TestMessageSignVerify(t *testing.T) {
message := "test message"
// Create the key.
generate := runEthkey(t, "generate", keyfile)
generate := runEthkey(t, "generate", "--lightkdf", keyfile)
generate.Expect(`
!! Unsupported terminal, password will be echoed.
Password: {{.InputLine "foobar"}}

View File

@@ -34,17 +34,22 @@ var disasmCommand = cli.Command{
}
func disasmCmd(ctx *cli.Context) error {
if len(ctx.Args().First()) == 0 {
return errors.New("filename required")
var in string
switch {
case len(ctx.Args().First()) > 0:
fn := ctx.Args().First()
input, err := ioutil.ReadFile(fn)
if err != nil {
return err
}
in = string(input)
case ctx.GlobalIsSet(InputFlag.Name):
in = ctx.GlobalString(InputFlag.Name)
default:
return errors.New("Missing filename or --input value")
}
fn := ctx.Args().First()
in, err := ioutil.ReadFile(fn)
if err != nil {
return err
}
code := strings.TrimSpace(string(in))
code := strings.TrimSpace(in)
fmt.Printf("%v\n", code)
return asm.PrintDisassembled(code)
}

View File

@@ -79,10 +79,18 @@ var (
Name: "input",
Usage: "input for the EVM",
}
InputFileFlag = cli.StringFlag{
Name: "inputfile",
Usage: "file containing input for the EVM",
}
VerbosityFlag = cli.IntFlag{
Name: "verbosity",
Usage: "sets the verbosity level",
}
BenchFlag = cli.BoolFlag{
Name: "bench",
Usage: "benchmark the execution",
}
CreateFlag = cli.BoolFlag{
Name: "create",
Usage: "indicates the action should be create rather than call",
@@ -120,6 +128,7 @@ var (
func init() {
app.Flags = []cli.Flag{
BenchFlag,
CreateFlag,
DebugFlag,
VerbosityFlag,
@@ -130,6 +139,7 @@ func init() {
ValueFlag,
DumpFlag,
InputFlag,
InputFileFlag,
MemProfileFlag,
CPUProfileFlag,
StatDumpFlag,
@@ -147,6 +157,7 @@ func init() {
runCommand,
stateTestCommand,
}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
}
func main() {

View File

@@ -25,6 +25,7 @@ import (
"os"
goruntime "runtime"
"runtime/pprof"
"testing"
"time"
"github.com/ethereum/go-ethereum/cmd/evm/internal/compiler"
@@ -69,6 +70,33 @@ func readGenesis(genesisPath string) *core.Genesis {
return genesis
}
func timedExec(bench bool, execFunc func() ([]byte, uint64, error)) ([]byte, uint64, time.Duration, error) {
var (
output []byte
gasLeft uint64
execTime time.Duration
err error
)
if bench {
result := testing.Benchmark(func(b *testing.B) {
for i := 0; i < b.N; i++ {
output, gasLeft, err = execFunc()
}
})
// Get the average execution time from the benchmarking result.
// There are other useful stats here that could be reported.
execTime = time.Duration(result.NsPerOp())
} else {
startTime := time.Now()
output, gasLeft, err = execFunc()
execTime = time.Since(startTime)
}
return output, gasLeft, execTime, err
}
func runCmd(ctx *cli.Context) error {
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
@@ -116,11 +144,7 @@ func runCmd(ctx *cli.Context) error {
receiver = common.HexToAddress(ctx.GlobalString(ReceiverFlag.Name))
}
var (
code []byte
ret []byte
err error
)
var code []byte
codeFileFlag := ctx.GlobalString(CodeFileFlag.Name)
codeFlag := ctx.GlobalString(CodeFlag.Name)
@@ -203,18 +227,36 @@ func runCmd(ctx *cli.Context) error {
} else {
runtimeConfig.ChainConfig = params.AllEthashProtocolChanges
}
tstart := time.Now()
var leftOverGas uint64
var hexInput []byte
if inputFileFlag := ctx.GlobalString(InputFileFlag.Name); inputFileFlag != "" {
var err error
if hexInput, err = ioutil.ReadFile(inputFileFlag); err != nil {
fmt.Printf("could not load input from file: %v\n", err)
os.Exit(1)
}
} else {
hexInput = []byte(ctx.GlobalString(InputFlag.Name))
}
input := common.FromHex(string(bytes.TrimSpace(hexInput)))
var execFunc func() ([]byte, uint64, error)
if ctx.GlobalBool(CreateFlag.Name) {
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
ret, _, leftOverGas, err = runtime.Create(input, &runtimeConfig)
input = append(code, input...)
execFunc = func() ([]byte, uint64, error) {
output, _, gasLeft, err := runtime.Create(input, &runtimeConfig)
return output, gasLeft, err
}
} else {
if len(code) > 0 {
statedb.SetCode(receiver, code)
}
ret, leftOverGas, err = runtime.Call(receiver, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtimeConfig)
execFunc = func() ([]byte, uint64, error) {
return runtime.Call(receiver, input, &runtimeConfig)
}
}
execTime := time.Since(tstart)
output, leftOverGas, execTime, err := timedExec(ctx.GlobalBool(BenchFlag.Name), execFunc)
if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit(true)
@@ -257,7 +299,7 @@ Gas used: %d
`, execTime, mem.HeapObjects, mem.Alloc, mem.TotalAlloc, mem.NumGC, initialGas-leftOverGas)
}
if tracer == nil {
fmt.Printf("0x%x\n", ret)
fmt.Printf("0x%x\n", output)
if err != nil {
fmt.Printf(" error: %v\n", err)
}

View File

@@ -58,7 +58,7 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/params"
"golang.org/x/net/websocket"
"github.com/gorilla/websocket"
)
var (
@@ -296,8 +296,7 @@ func (f *faucet) listenAndServe(port int) error {
go f.loop()
http.HandleFunc("/", f.webHandler)
http.Handle("/api", websocket.Handler(f.apiHandler))
http.HandleFunc("/api", f.apiHandler)
return http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}
@@ -308,7 +307,13 @@ func (f *faucet) webHandler(w http.ResponseWriter, r *http.Request) {
}
// apiHandler handles requests for Ether grants and transaction statuses.
func (f *faucet) apiHandler(conn *websocket.Conn) {
func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
upgrader := websocket.Upgrader{}
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
// Start tracking the connection and drop at the end
defer conn.Close()
@@ -331,7 +336,6 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
head *types.Header
balance *big.Int
nonce uint64
err error
)
for head == nil || balance == nil {
// Retrieve the current stats cached by the faucet
@@ -347,6 +351,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
if head == nil || balance == nil {
// Report the faucet offline until initial stats are ready
//lint:ignore ST1005 This error is to be displayed in the browser
if err = sendError(conn, errors.New("Faucet offline")); err != nil {
log.Warn("Failed to send faucet error to client", "err", err)
return
@@ -355,11 +360,14 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
}
}
// Send over the initial stats and the latest header
f.lock.RLock()
reqs := f.reqs
f.lock.RUnlock()
if err = send(conn, map[string]interface{}{
"funds": new(big.Int).Div(balance, ether),
"funded": nonce,
"peers": f.stack.Server().PeerCount(),
"requests": f.reqs,
"requests": reqs,
}, 3*time.Second); err != nil {
log.Warn("Failed to send initial stats to client", "err", err)
return
@@ -376,7 +384,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
Tier uint `json:"tier"`
Captcha string `json:"captcha"`
}
if err = websocket.JSON.Receive(conn, &msg); err != nil {
if err = conn.ReadJSON(&msg); err != nil {
return
}
if !*noauthFlag && !strings.HasPrefix(msg.URL, "https://gist.github.com/") && !strings.HasPrefix(msg.URL, "https://twitter.com/") &&
@@ -388,6 +396,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
continue
}
if msg.Tier >= uint(*tiersFlag) {
//lint:ignore ST1005 This error is to be displayed in the browser
if err = sendError(conn, errors.New("Invalid funding tier requested")); err != nil {
log.Warn("Failed to send tier error to client", "err", err)
return
@@ -425,6 +434,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
}
if !result.Success {
log.Warn("Captcha verification failed", "err", string(result.Errors))
//lint:ignore ST1005 it's funny and the robot won't mind
if err = sendError(conn, errors.New("Beep-bop, you're a robot!")); err != nil {
log.Warn("Failed to send captcha failure to client", "err", err)
return
@@ -446,6 +456,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
}
continue
case strings.HasPrefix(msg.URL, "https://plus.google.com/"):
//lint:ignore ST1005 Google is a company name and should be capitalized.
if err = sendError(conn, errors.New("Google+ authentication discontinued as the service was sunset")); err != nil {
log.Warn("Failed to send Google+ deprecation to client", "err", err)
return
@@ -458,6 +469,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
case *noauthFlag:
username, avatar, address, err = authNoAuth(msg.URL)
default:
//lint:ignore ST1005 This error is to be displayed in the browser
err = errors.New("Something funky happened, please open an issue at https://github.com/ethereum/go-ethereum/issues")
}
if err != nil {
@@ -516,7 +528,7 @@ func (f *faucet) apiHandler(conn *websocket.Conn) {
// Send an error if too frequent funding, othewise a success
if !fund {
if err = sendError(conn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(timeout.Sub(time.Now())))); err != nil { // nolint: gosimple
if err = sendError(conn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { // nolint: gosimple
log.Warn("Failed to send funding error to client", "err", err)
return
}
@@ -657,7 +669,7 @@ func send(conn *websocket.Conn, value interface{}, timeout time.Duration) error
timeout = 60 * time.Second
}
conn.SetWriteDeadline(time.Now().Add(timeout))
return websocket.JSON.Send(conn, value)
return conn.WriteJSON(value)
}
// sendError transmits an error to the remote end of the websocket, also setting
@@ -678,6 +690,7 @@ func authTwitter(url string) (string, string, common.Address, error) {
// Ensure the user specified a meaningful URL, no fancy nonsense
parts := strings.Split(url, "/")
if len(parts) < 4 || parts[len(parts)-2] != "status" {
//lint:ignore ST1005 This error is to be displayed in the browser
return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
}
// Twitter's API isn't really friendly with direct links. Still, we don't
@@ -692,6 +705,7 @@ func authTwitter(url string) (string, string, common.Address, error) {
// Resolve the username from the final redirect, no intermediate junk
parts = strings.Split(res.Request.URL.String(), "/")
if len(parts) < 4 || parts[len(parts)-2] != "status" {
//lint:ignore ST1005 This error is to be displayed in the browser
return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
}
username := parts[len(parts)-3]
@@ -702,6 +716,7 @@ func authTwitter(url string) (string, string, common.Address, error) {
}
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
if address == (common.Address{}) {
//lint:ignore ST1005 This error is to be displayed in the browser
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
}
var avatar string
@@ -717,6 +732,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
// Ensure the user specified a meaningful URL, no fancy nonsense
parts := strings.Split(url, "/")
if len(parts) < 4 || parts[len(parts)-2] != "posts" {
//lint:ignore ST1005 This error is to be displayed in the browser
return "", "", common.Address{}, errors.New("Invalid Facebook post URL")
}
username := parts[len(parts)-3]
@@ -736,6 +752,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
}
address := common.HexToAddress(string(regexp.MustCompile("0x[0-9a-fA-F]{40}").Find(body)))
if address == (common.Address{}) {
//lint:ignore ST1005 This error is to be displayed in the browser
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
}
var avatar string
@@ -751,6 +768,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
func authNoAuth(url string) (string, string, common.Address, error) {
address := common.HexToAddress(regexp.MustCompile("0x[0-9a-fA-F]{40}").FindString(url))
if address == (common.Address{}) {
//lint:ignore ST1005 This error is to be displayed in the browser
return "", "", common.Address{}, errors.New("No Ethereum address found to fund")
}
return address.Hex() + "@noauth", "", address, nil

View File

@@ -56,6 +56,18 @@ This is a destructive action and changes the network in which you will be
participating.
It expects the genesis file as argument.`,
}
dumpGenesisCommand = cli.Command{
Action: utils.MigrateFlags(dumpGenesis),
Name: "dumpgenesis",
Usage: "Dumps genesis block JSON configuration to stdout",
ArgsUsage: "",
Flags: []cli.Flag{
utils.DataDirFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
The dumpgenesis command dumps the genesis block configuration in JSON format to stdout.`,
}
importCommand = cli.Command{
Action: utils.MigrateFlags(importChain),
@@ -227,6 +239,17 @@ func initGenesis(ctx *cli.Context) error {
return nil
}
func dumpGenesis(ctx *cli.Context) error {
genesis := utils.MakeGenesis(ctx)
if genesis == nil {
genesis = core.DefaultGenesisBlock()
}
if err := json.NewEncoder(os.Stdout).Encode(genesis); err != nil {
utils.Fatalf("could not encode genesis")
}
return nil
}
func importChain(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")

View File

@@ -28,7 +28,6 @@ import (
cli "gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/dashboard"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
@@ -75,11 +74,10 @@ type ethstatsConfig struct {
}
type gethConfig struct {
Eth eth.Config
Shh whisper.Config
Node node.Config
Ethstats ethstatsConfig
Dashboard dashboard.Config
Eth eth.Config
Shh whisper.Config
Node node.Config
Ethstats ethstatsConfig
}
func loadConfig(file string, cfg *gethConfig) error {
@@ -101,8 +99,8 @@ func defaultNodeConfig() node.Config {
cfg := node.DefaultConfig
cfg.Name = clientIdentifier
cfg.Version = params.VersionWithCommit(gitCommit, gitDate)
cfg.HTTPModules = append(cfg.HTTPModules, "eth", "shh")
cfg.WSModules = append(cfg.WSModules, "eth", "shh")
cfg.HTTPModules = append(cfg.HTTPModules, "eth")
cfg.WSModules = append(cfg.WSModules, "eth")
cfg.IPCPath = "geth.ipc"
return cfg
}
@@ -110,10 +108,9 @@ func defaultNodeConfig() node.Config {
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
// Load defaults.
cfg := gethConfig{
Eth: eth.DefaultConfig,
Shh: whisper.DefaultConfig,
Node: defaultNodeConfig(),
Dashboard: dashboard.DefaultConfig,
Eth: eth.DefaultConfig,
Shh: whisper.DefaultConfig,
Node: defaultNodeConfig(),
}
// Load config file.
@@ -134,7 +131,6 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
}
utils.SetShhConfig(ctx, stack, &cfg.Shh)
utils.SetDashboardConfig(ctx, &cfg.Dashboard)
return stack, cfg
}
@@ -154,11 +150,11 @@ func makeFullNode(ctx *cli.Context) *node.Node {
if ctx.GlobalIsSet(utils.OverrideIstanbulFlag.Name) {
cfg.Eth.OverrideIstanbul = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideIstanbulFlag.Name))
}
if ctx.GlobalIsSet(utils.OverrideMuirGlacierFlag.Name) {
cfg.Eth.OverrideMuirGlacier = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideMuirGlacierFlag.Name))
}
utils.RegisterEthService(stack, &cfg.Eth)
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
utils.RegisterDashboardService(stack, &cfg.Dashboard, gitCommit)
}
// Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
shhEnabled := enableWhisper(ctx)
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DeveloperFlag.Name)

View File

@@ -51,7 +51,9 @@ func TestConsoleWelcome(t *testing.T) {
geth.SetTemplateFunc("goarch", func() string { return runtime.GOARCH })
geth.SetTemplateFunc("gover", runtime.Version)
geth.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
geth.SetTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
geth.SetTemplateFunc("niltime", func() string {
return time.Unix(0, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
})
geth.SetTemplateFunc("apis", func() string { return ipcAPIs })
// Verify the actual welcome message to the required template
@@ -87,11 +89,14 @@ func TestIPCAttachWelcome(t *testing.T) {
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--shh", "--ipcpath", ipc)
defer func() {
geth.Interrupt()
geth.ExpectExit()
}()
waitForEndpoint(t, ipc, 3*time.Second)
testAttachWelcome(t, geth, "ipc:"+ipc, ipcAPIs)
geth.Interrupt()
geth.ExpectExit()
}
func TestHTTPAttachWelcome(t *testing.T) {
@@ -100,13 +105,14 @@ func TestHTTPAttachWelcome(t *testing.T) {
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--rpc", "--rpcport", port)
defer func() {
geth.Interrupt()
geth.ExpectExit()
}()
endpoint := "http://127.0.0.1:" + port
waitForEndpoint(t, endpoint, 3*time.Second)
testAttachWelcome(t, geth, endpoint, httpAPIs)
geth.Interrupt()
geth.ExpectExit()
}
func TestWSAttachWelcome(t *testing.T) {
@@ -116,13 +122,14 @@ func TestWSAttachWelcome(t *testing.T) {
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--ws", "--wsport", port)
defer func() {
geth.Interrupt()
geth.ExpectExit()
}()
endpoint := "ws://127.0.0.1:" + port
waitForEndpoint(t, endpoint, 3*time.Second)
testAttachWelcome(t, geth, endpoint, httpAPIs)
geth.Interrupt()
geth.ExpectExit()
}
func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
@@ -137,7 +144,9 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint, apis string) {
attach.SetTemplateFunc("gover", runtime.Version)
attach.SetTemplateFunc("gethver", func() string { return params.VersionWithCommit("", "") })
attach.SetTemplateFunc("etherbase", func() string { return geth.Etherbase })
attach.SetTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
attach.SetTemplateFunc("niltime", func() string {
return time.Unix(0, 0).Format("Mon Jan 02 2006 15:04:05 GMT-0700 (MST)")
})
attach.SetTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
attach.SetTemplateFunc("datadir", func() string { return geth.Datadir })
attach.SetTemplateFunc("apis", func() string { return apis })

View File

@@ -70,10 +70,7 @@ var (
utils.NoUSBFlag,
utils.SmartCardDaemonPathFlag,
utils.OverrideIstanbulFlag,
utils.DashboardEnabledFlag,
utils.DashboardAddrFlag,
utils.DashboardPortFlag,
utils.DashboardRefreshFlag,
utils.OverrideMuirGlacierFlag,
utils.EthashCacheDirFlag,
utils.EthashCachesInMemoryFlag,
utils.EthashCachesOnDiskFlag,
@@ -134,6 +131,7 @@ var (
utils.NetrestrictFlag,
utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag,
utils.DNSDiscoveryFlag,
utils.DeveloperFlag,
utils.DeveloperPeriodFlag,
utils.TestnetFlag,
@@ -197,7 +195,7 @@ func init() {
// Initialize the CLI app and start Geth
app.Action = geth
app.HideVersion = true // we have a command to print the version
app.Copyright = "Copyright 2013-2019 The go-ethereum Authors"
app.Copyright = "Copyright 2013-2020 The go-ethereum Authors"
app.Commands = []cli.Command{
// See chaincmd.go:
initCommand,
@@ -208,6 +206,7 @@ func init() {
copydbCommand,
removedbCommand,
dumpCommand,
dumpGenesisCommand,
inspectCommand,
// See accountcmd.go:
accountCommand,
@@ -236,16 +235,8 @@ func init() {
app.Flags = append(app.Flags, metricsFlags...)
app.Before = func(ctx *cli.Context) error {
logdir := ""
if ctx.GlobalBool(utils.DashboardEnabledFlag.Name) {
logdir = (&node.Config{DataDir: utils.MakeDataDir(ctx)}).ResolvePath("logs")
}
if err := debug.Setup(ctx, logdir); err != nil {
return err
}
return nil
return debug.Setup(ctx)
}
app.After = func(ctx *cli.Context) error {
debug.Exit()
console.Stdin.Close() // Resets terminal mode.

View File

@@ -117,7 +117,6 @@ func version(ctx *cli.Context) error {
}
fmt.Println("Architecture:", runtime.GOARCH)
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
fmt.Println("Network Id:", eth.DefaultConfig.NetworkId)
fmt.Println("Go Version:", runtime.Version())
fmt.Println("Operating System:", runtime.GOOS)
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))

View File

@@ -80,6 +80,7 @@ type RetestethEthAPI interface {
SendRawTransaction(ctx context.Context, rawTx hexutil.Bytes) (common.Hash, error)
BlockNumber(ctx context.Context) (uint64, error)
GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error)
GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error)
GetBalance(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (*math.HexOrDecimal256, error)
GetCode(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (hexutil.Bytes, error)
GetTransactionCount(ctx context.Context, address common.Address, blockNr math.HexOrDecimal64) (uint64, error)
@@ -110,7 +111,6 @@ type RetestethAPI struct {
genesisHash common.Hash
engine *NoRewardEngine
blockchain *core.BlockChain
blockNumber uint64
txMap map[common.Address]map[uint64]*types.Transaction // Sender -> Nonce -> Transaction
txSenders map[common.Address]struct{} // Set of transaction senders
blockInterval uint64
@@ -356,7 +356,7 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
ChainID: chainId,
HomesteadBlock: homesteadBlock,
DAOForkBlock: daoForkBlock,
DAOForkSupport: false,
DAOForkSupport: true,
EIP150Block: eip150Block,
EIP155Block: eip155Block,
EIP158Block: eip158Block,
@@ -411,7 +411,6 @@ func (api *RetestethAPI) SetChainParams(ctx context.Context, chainParams ChainPa
api.engine = engine
api.blockchain = blockchain
api.db = state.NewDatabase(api.ethDb)
api.blockNumber = 0
api.txMap = make(map[common.Address]map[uint64]*types.Transaction)
api.txSenders = make(map[common.Address]struct{})
api.blockInterval = 0
@@ -424,7 +423,7 @@ func (api *RetestethAPI) SendRawTransaction(ctx context.Context, rawTx hexutil.B
// Return nil is not by mistake - some tests include sending transaction where gasLimit overflows uint64
return common.Hash{}, nil
}
signer := types.MakeSigner(api.chainConfig, big.NewInt(int64(api.blockNumber)))
signer := types.MakeSigner(api.chainConfig, big.NewInt(int64(api.currentNumber())))
sender, err := types.Sender(signer, tx)
if err != nil {
return common.Hash{}, err
@@ -450,9 +449,17 @@ func (api *RetestethAPI) MineBlocks(ctx context.Context, number uint64) (bool, e
return true, nil
}
func (api *RetestethAPI) currentNumber() uint64 {
if current := api.blockchain.CurrentBlock(); current != nil {
return current.NumberU64()
}
return 0
}
func (api *RetestethAPI) mineBlock() error {
parentHash := rawdb.ReadCanonicalHash(api.ethDb, api.blockNumber)
parent := rawdb.ReadBlock(api.ethDb, parentHash, api.blockNumber)
number := api.currentNumber()
parentHash := rawdb.ReadCanonicalHash(api.ethDb, number)
parent := rawdb.ReadBlock(api.ethDb, parentHash, number)
var timestamp uint64
if api.blockInterval == 0 {
timestamp = uint64(time.Now().Unix())
@@ -462,7 +469,7 @@ func (api *RetestethAPI) mineBlock() error {
gasLimit := core.CalcGasLimit(parent, 9223372036854775807, 9223372036854775807)
header := &types.Header{
ParentHash: parent.Hash(),
Number: big.NewInt(int64(api.blockNumber + 1)),
Number: big.NewInt(int64(number + 1)),
GasLimit: gasLimit,
Extra: api.extraData,
Time: timestamp,
@@ -495,7 +502,6 @@ func (api *RetestethAPI) mineBlock() error {
txCount := 0
var txs []*types.Transaction
var receipts []*types.Receipt
var coalescedLogs []*types.Log
var blockFull = gasPool.Gas() < params.TxGas
for address := range api.txSenders {
if blockFull {
@@ -522,7 +528,6 @@ func (api *RetestethAPI) mineBlock() error {
}
txs = append(txs, tx)
receipts = append(receipts, receipt)
coalescedLogs = append(coalescedLogs, receipt.Logs...)
delete(m, nonce)
if len(m) == 0 {
// Last tx for the sender
@@ -550,8 +555,7 @@ func (api *RetestethAPI) importBlock(block *types.Block) error {
if _, err := api.blockchain.InsertChain([]*types.Block{block}); err != nil {
return err
}
api.blockNumber = block.NumberU64()
fmt.Printf("Imported block %d\n", block.NumberU64())
fmt.Printf("Imported block %d, head is %d\n", block.NumberU64(), api.currentNumber())
return nil
}
@@ -576,7 +580,9 @@ func (api *RetestethAPI) RewindToBlock(ctx context.Context, newHead uint64) (boo
if err := api.blockchain.SetHead(newHead); err != nil {
return false, err
}
api.blockNumber = newHead
// When we rewind, the transaction pool should be cleaned out.
api.txMap = make(map[common.Address]map[uint64]*types.Transaction)
api.txSenders = make(map[common.Address]struct{})
return true, nil
}
@@ -596,8 +602,7 @@ func (api *RetestethAPI) GetLogHash(ctx context.Context, txHash common.Hash) (co
}
func (api *RetestethAPI) BlockNumber(ctx context.Context) (uint64, error) {
//fmt.Printf("BlockNumber, response: %d\n", api.blockNumber)
return api.blockNumber, nil
return api.currentNumber(), nil
}
func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexOrDecimal64, fullTx bool) (map[string]interface{}, error) {
@@ -614,6 +619,20 @@ func (api *RetestethAPI) GetBlockByNumber(ctx context.Context, blockNr math.HexO
return nil, fmt.Errorf("block %d not found", blockNr)
}
func (api *RetestethAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) {
block := api.blockchain.GetBlockByHash(blockHash)
if block != nil {
response, err := RPCMarshalBlock(block, true, fullTx)
if err != nil {
return nil, err
}
response["author"] = response["miner"]
response["totalDifficulty"] = (*hexutil.Big)(api.blockchain.GetTd(block.Hash(), block.Number().Uint64()))
return response, err
}
return nil, fmt.Errorf("block 0x%x not found", blockHash)
}
func (api *RetestethAPI) AccountRange(ctx context.Context,
blockHashOrNumber *math.HexOrDecimal256, txIndex uint64,
addressHash *math.HexOrDecimal256, maxResults uint64,
@@ -682,9 +701,6 @@ func (api *RetestethAPI) AccountRange(ctx context.Context,
for i := 0; i < int(maxResults) && it.Next(); i++ {
if preimage := accountTrie.GetKey(it.Key); preimage != nil {
result.AddressMap[common.BytesToHash(it.Key)] = common.BytesToAddress(preimage)
//fmt.Printf("%x: %x\n", it.Key, preimage)
} else {
//fmt.Printf("could not find preimage for %x\n", it.Key)
}
}
//fmt.Printf("Number of entries returned: %d\n", len(result.AddressMap))
@@ -808,9 +824,6 @@ func (api *RetestethAPI) StorageRangeAt(ctx context.Context,
Key: string(ks),
Value: string(vs),
}
//fmt.Printf("Key: %s, Value: %s\n", ks, vs)
} else {
//fmt.Printf("Did not find preimage for %x\n", it.Key)
}
}
if it.Next() {
@@ -889,7 +902,7 @@ func retesteth(ctx *cli.Context) error {
log.Info("HTTP endpoint closed", "url", httpEndpoint)
}()
abortChan := make(chan os.Signal)
abortChan := make(chan os.Signal, 11)
signal.Notify(abortChan, os.Interrupt)
sig := <-abortChan

View File

@@ -22,8 +22,6 @@ import (
"io"
"sort"
"strings"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/debug"
cli "gopkg.in/urfave/cli.v1"
@@ -116,16 +114,6 @@ var AppHelpFlagGroups = []flagGroup{
utils.EthashDatasetsOnDiskFlag,
},
},
//{
// Name: "DASHBOARD",
// Flags: []cli.Flag{
// utils.DashboardEnabledFlag,
// utils.DashboardAddrFlag,
// utils.DashboardPortFlag,
// utils.DashboardRefreshFlag,
// utils.DashboardAssetsFlag,
// },
//},
{
Name: "TRANSACTION POOL",
Flags: []cli.Flag{
@@ -194,6 +182,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.BootnodesFlag,
utils.BootnodesV4Flag,
utils.BootnodesV5Flag,
utils.DNSDiscoveryFlag,
utils.ListenPortFlag,
utils.MaxPeersFlag,
utils.MaxPendingPeersFlag,
@@ -324,9 +313,6 @@ func init() {
var uncategorized []cli.Flag
for _, flag := range data.(*cli.App).Flags {
if _, ok := categorized[flag.String()]; !ok {
if strings.HasPrefix(flag.GetName(), "dashboard") {
continue
}
uncategorized = append(uncategorized, flag)
}
}

View File

@@ -17,7 +17,6 @@
package main
import (
"encoding/binary"
"errors"
"math"
"math/big"
@@ -28,6 +27,7 @@ import (
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/core/types"
"github.com/ethereum/go-ethereum/params"
)
@@ -60,14 +60,14 @@ type alethGenesisSpec struct {
} `json:"params"`
Genesis struct {
Nonce hexutil.Bytes `json:"nonce"`
Difficulty *hexutil.Big `json:"difficulty"`
MixHash common.Hash `json:"mixHash"`
Author common.Address `json:"author"`
Timestamp hexutil.Uint64 `json:"timestamp"`
ParentHash common.Hash `json:"parentHash"`
ExtraData hexutil.Bytes `json:"extraData"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
Nonce types.BlockNonce `json:"nonce"`
Difficulty *hexutil.Big `json:"difficulty"`
MixHash common.Hash `json:"mixHash"`
Author common.Address `json:"author"`
Timestamp hexutil.Uint64 `json:"timestamp"`
ParentHash common.Hash `json:"parentHash"`
ExtraData hexutil.Bytes `json:"extraData"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
} `json:"genesis"`
Accounts map[common.UnprefixedAddress]*alethGenesisSpecAccount `json:"accounts"`
@@ -146,9 +146,7 @@ func newAlethGenesisSpec(network string, genesis *core.Genesis) (*alethGenesisSp
spec.Params.DurationLimit = (*math2.HexOrDecimal256)(params.DurationLimit)
spec.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
spec.Genesis.Nonce = (hexutil.Bytes)(make([]byte, 8))
binary.LittleEndian.PutUint64(spec.Genesis.Nonce[:], genesis.Nonce)
spec.Genesis.Nonce = types.EncodeNonce(genesis.Nonce)
spec.Genesis.MixHash = genesis.Mixhash
spec.Genesis.Difficulty = (*hexutil.Big)(genesis.Difficulty)
spec.Genesis.Author = genesis.Coinbase
@@ -278,8 +276,8 @@ type parityChainSpec struct {
Genesis struct {
Seal struct {
Ethereum struct {
Nonce hexutil.Bytes `json:"nonce"`
MixHash hexutil.Bytes `json:"mixHash"`
Nonce types.BlockNonce `json:"nonce"`
MixHash hexutil.Bytes `json:"mixHash"`
} `json:"ethereum"`
} `json:"seal"`
@@ -305,19 +303,20 @@ type parityChainSpecAccount struct {
// parityChainSpecBuiltin is the precompiled contract definition.
type parityChainSpecBuiltin struct {
Name string `json:"name"` // Each builtin should has it own name
Pricing *parityChainSpecPricing `json:"pricing"` // Each builtin should has it own price strategy
ActivateAt *hexutil.Big `json:"activate_at,omitempty"` // ActivateAt can't be omitted if empty, default means no fork
EIP1108Transition *hexutil.Big `json:"eip1108_transition,omitempty"` // EIP1108Transition can't be omitted if empty, default means no fork
Name string `json:"name"` // Each builtin should has it own name
Pricing interface{} `json:"pricing"` // Each builtin should has it own price strategy
ActivateAt *hexutil.Big `json:"activate_at,omitempty"` // ActivateAt can't be omitted if empty, default means no fork
}
// parityChainSpecPricing represents the different pricing models that builtin
// contracts might advertise using.
type parityChainSpecPricing struct {
Linear *parityChainSpecLinearPricing `json:"linear,omitempty"`
ModExp *parityChainSpecModExpPricing `json:"modexp,omitempty"`
AltBnPairing *parityChainSpecAltBnPairingPricing `json:"alt_bn128_pairing,omitempty"`
AltBnConstOperation *parityChainSpecAltBnConstOperationPricing `json:"alt_bn128_const_operations,omitempty"`
Linear *parityChainSpecLinearPricing `json:"linear,omitempty"`
ModExp *parityChainSpecModExpPricing `json:"modexp,omitempty"`
// Before the https://github.com/paritytech/parity-ethereum/pull/11039,
// Parity uses this format to config bn pairing price policy.
AltBnPairing *parityChainSepcAltBnPairingPricing `json:"alt_bn128_pairing,omitempty"`
// Blake2F is the price per round of Blake2 compression
Blake2F *parityChainSpecBlakePricing `json:"blake2_f,omitempty"`
@@ -332,22 +331,36 @@ type parityChainSpecModExpPricing struct {
Divisor uint64 `json:"divisor"`
}
// parityChainSpecAltBnConstOperationPricing defines the price
// policy for bn const operation(used after istanbul)
type parityChainSpecAltBnConstOperationPricing struct {
Price uint64 `json:"price"`
EIP1108TransitionPrice uint64 `json:"eip1108_transition_price,omitempty"` // Before Istanbul fork, this field is nil
Price uint64 `json:"price"`
}
type parityChainSpecAltBnPairingPricing struct {
Base uint64 `json:"base"`
Pair uint64 `json:"pair"`
EIP1108TransitionBase uint64 `json:"eip1108_transition_base,omitempty"` // Before Istanbul fork, this field is nil
EIP1108TransitionPair uint64 `json:"eip1108_transition_pair,omitempty"` // Before Istanbul fork, this field is nil
// parityChainSepcAltBnPairingPricing defines the price policy
// for bn pairing.
type parityChainSepcAltBnPairingPricing struct {
Base uint64 `json:"base"`
Pair uint64 `json:"pair"`
}
// parityChainSpecBlakePricing defines the price policy for blake2 f
// compression.
type parityChainSpecBlakePricing struct {
GasPerRound uint64 `json:"gas_per_round"`
}
type parityChainSpecAlternativePrice struct {
AltBnConstOperationPrice *parityChainSpecAltBnConstOperationPricing `json:"alt_bn128_const_operations,omitempty"`
AltBnPairingPrice *parityChainSepcAltBnPairingPricing `json:"alt_bn128_pairing,omitempty"`
}
// parityChainSpecVersionedPricing represents a single version price policy.
type parityChainSpecVersionedPricing struct {
Price *parityChainSpecAlternativePrice `json:"price,omitempty"`
Info string `json:"info,omitempty"`
}
// newParityChainSpec converts a go-ethereum genesis block into a Parity specific
// chain specification format.
func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []string) (*parityChainSpec, error) {
@@ -411,10 +424,8 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
// 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)
spec.Genesis.Seal.Ethereum.MixHash = (hexutil.Bytes)(genesis.Mixhash[:])
spec.Genesis.Seal.Ethereum.Nonce = types.EncodeNonce(genesis.Nonce)
spec.Genesis.Seal.Ethereum.MixHash = (genesis.Mixhash[:])
spec.Genesis.Difficulty = (*hexutil.Big)(genesis.Difficulty)
spec.Genesis.Author = genesis.Coinbase
spec.Genesis.Timestamp = (hexutil.Uint64)(genesis.Timestamp)
@@ -445,16 +456,32 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
})
if genesis.Config.ByzantiumBlock != nil {
spec.setPrecompile(5, &parityChainSpecBuiltin{
Name: "modexp", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), Pricing: &parityChainSpecPricing{ModExp: &parityChainSpecModExpPricing{Divisor: 20}},
Name: "modexp",
ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
Pricing: &parityChainSpecPricing{
ModExp: &parityChainSpecModExpPricing{Divisor: 20},
},
})
spec.setPrecompile(6, &parityChainSpecBuiltin{
Name: "alt_bn128_add", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), Pricing: &parityChainSpecPricing{AltBnConstOperation: &parityChainSpecAltBnConstOperationPricing{Price: 500}},
Name: "alt_bn128_add",
ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
Pricing: &parityChainSpecPricing{
Linear: &parityChainSpecLinearPricing{Base: 500, Word: 0},
},
})
spec.setPrecompile(7, &parityChainSpecBuiltin{
Name: "alt_bn128_mul", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), Pricing: &parityChainSpecPricing{AltBnConstOperation: &parityChainSpecAltBnConstOperationPricing{Price: 40000}},
Name: "alt_bn128_mul",
ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
Pricing: &parityChainSpecPricing{
Linear: &parityChainSpecLinearPricing{Base: 40000, Word: 0},
},
})
spec.setPrecompile(8, &parityChainSpecBuiltin{
Name: "alt_bn128_pairing", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000}},
Name: "alt_bn128_pairing",
ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
Pricing: &parityChainSpecPricing{
AltBnPairing: &parityChainSepcAltBnPairingPricing{Base: 100000, Pair: 80000},
},
})
}
if genesis.Config.IstanbulBlock != nil {
@@ -462,16 +489,59 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
return nil, errors.New("invalid genesis, istanbul fork is enabled while byzantium is not")
}
spec.setPrecompile(6, &parityChainSpecBuiltin{
Name: "alt_bn128_add", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), EIP1108Transition: (*hexutil.Big)(genesis.Config.IstanbulBlock), Pricing: &parityChainSpecPricing{AltBnConstOperation: &parityChainSpecAltBnConstOperationPricing{Price: 500, EIP1108TransitionPrice: 150}},
Name: "alt_bn128_add",
ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
Pricing: map[*hexutil.Big]*parityChainSpecVersionedPricing{
(*hexutil.Big)(big.NewInt(0)): {
Price: &parityChainSpecAlternativePrice{
AltBnConstOperationPrice: &parityChainSpecAltBnConstOperationPricing{Price: 500},
},
},
(*hexutil.Big)(genesis.Config.IstanbulBlock): {
Price: &parityChainSpecAlternativePrice{
AltBnConstOperationPrice: &parityChainSpecAltBnConstOperationPricing{Price: 150},
},
},
},
})
spec.setPrecompile(7, &parityChainSpecBuiltin{
Name: "alt_bn128_mul", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), EIP1108Transition: (*hexutil.Big)(genesis.Config.IstanbulBlock), Pricing: &parityChainSpecPricing{AltBnConstOperation: &parityChainSpecAltBnConstOperationPricing{Price: 40000, EIP1108TransitionPrice: 6000}},
Name: "alt_bn128_mul",
ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
Pricing: map[*hexutil.Big]*parityChainSpecVersionedPricing{
(*hexutil.Big)(big.NewInt(0)): {
Price: &parityChainSpecAlternativePrice{
AltBnConstOperationPrice: &parityChainSpecAltBnConstOperationPricing{Price: 40000},
},
},
(*hexutil.Big)(genesis.Config.IstanbulBlock): {
Price: &parityChainSpecAlternativePrice{
AltBnConstOperationPrice: &parityChainSpecAltBnConstOperationPricing{Price: 6000},
},
},
},
})
spec.setPrecompile(8, &parityChainSpecBuiltin{
Name: "alt_bn128_pairing", ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock), EIP1108Transition: (*hexutil.Big)(genesis.Config.IstanbulBlock), Pricing: &parityChainSpecPricing{AltBnPairing: &parityChainSpecAltBnPairingPricing{Base: 100000, Pair: 80000, EIP1108TransitionBase: 45000, EIP1108TransitionPair: 34000}},
Name: "alt_bn128_pairing",
ActivateAt: (*hexutil.Big)(genesis.Config.ByzantiumBlock),
Pricing: map[*hexutil.Big]*parityChainSpecVersionedPricing{
(*hexutil.Big)(big.NewInt(0)): {
Price: &parityChainSpecAlternativePrice{
AltBnPairingPrice: &parityChainSepcAltBnPairingPricing{Base: 100000, Pair: 80000},
},
},
(*hexutil.Big)(genesis.Config.IstanbulBlock): {
Price: &parityChainSpecAlternativePrice{
AltBnPairingPrice: &parityChainSepcAltBnPairingPricing{Base: 45000, Pair: 34000},
},
},
},
})
spec.setPrecompile(9, &parityChainSpecBuiltin{
Name: "blake2_f", ActivateAt: (*hexutil.Big)(genesis.Config.IstanbulBlock), Pricing: &parityChainSpecPricing{Blake2F: &parityChainSpecBlakePricing{GasPerRound: 1}},
Name: "blake2_f",
ActivateAt: (*hexutil.Big)(genesis.Config.IstanbulBlock),
Pricing: &parityChainSpecPricing{
Blake2F: &parityChainSpecBlakePricing{GasPerRound: 1},
},
})
}
return spec, nil
@@ -514,8 +584,6 @@ func (spec *parityChainSpec) setConstantinopleFix(num *big.Int) {
}
func (spec *parityChainSpec) setIstanbul(num *big.Int) {
// spec.Params.EIP152Transition = hexutil.Uint64(num.Uint64())
// spec.Params.EIP1108Transition = hexutil.Uint64(num.Uint64())
spec.Params.EIP1344Transition = hexutil.Uint64(num.Uint64())
spec.Params.EIP1884Transition = hexutil.Uint64(num.Uint64())
spec.Params.EIP2028Transition = hexutil.Uint64(num.Uint64())
@@ -525,7 +593,7 @@ func (spec *parityChainSpec) setIstanbul(num *big.Int) {
// pyEthereumGenesisSpec represents the genesis specification format used by the
// Python Ethereum implementation.
type pyEthereumGenesisSpec struct {
Nonce hexutil.Bytes `json:"nonce"`
Nonce types.BlockNonce `json:"nonce"`
Timestamp hexutil.Uint64 `json:"timestamp"`
ExtraData hexutil.Bytes `json:"extraData"`
GasLimit hexutil.Uint64 `json:"gasLimit"`
@@ -544,6 +612,7 @@ func newPyEthereumGenesisSpec(network string, genesis *core.Genesis) (*pyEthereu
return nil, errors.New("unsupported consensus engine")
}
spec := &pyEthereumGenesisSpec{
Nonce: types.EncodeNonce(genesis.Nonce),
Timestamp: (hexutil.Uint64)(genesis.Timestamp),
ExtraData: genesis.ExtraData,
GasLimit: (hexutil.Uint64)(genesis.GasLimit),
@@ -553,8 +622,5 @@ func newPyEthereumGenesisSpec(network string, genesis *core.Genesis) (*pyEthereu
Alloc: genesis.Alloc,
ParentHash: genesis.ParentHash,
}
spec.Nonce = (hexutil.Bytes)(make([]byte, 8))
binary.LittleEndian.PutUint64(spec.Nonce[:], genesis.Nonce)
return spec, nil
}

View File

@@ -17,6 +17,7 @@
package main
import (
"bytes"
"encoding/json"
"io/ioutil"
"reflect"
@@ -80,29 +81,15 @@ func TestParitySturebyConverter(t *testing.T) {
if err != nil {
t.Fatalf("failed creating chainspec: %v", err)
}
enc, err := json.MarshalIndent(spec, "", " ")
if err != nil {
t.Fatalf("failed encoding 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] {
t.Logf("got: %v\nexp: %v\n", exp[i], got[i])
}
}
if !bytes.Equal(expBlob, enc) {
t.Fatalf("chainspec mismatch")
}
}

View File

@@ -41,7 +41,7 @@ var dashboardContent = `
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{.NetworkTitle}}: Ethereum Testnet</title>
<title>{{.NetworkTitle}}: Network Dashboard</title>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

View File

@@ -129,15 +129,20 @@ func dial(server string, pubkey []byte) (*sshClient, error) {
fmt.Printf("SSH key fingerprint is %s [MD5]\n", ssh.FingerprintLegacyMD5(key))
fmt.Printf("Are you sure you want to continue connecting (yes/no)? ")
text, err := bufio.NewReader(os.Stdin).ReadString('\n')
switch {
case err != nil:
return err
case strings.TrimSpace(text) == "yes":
pubkey = key.Marshal()
return nil
default:
return fmt.Errorf("unknown auth choice: %v", text)
for {
text, err := bufio.NewReader(os.Stdin).ReadString('\n')
switch {
case err != nil:
return err
case strings.TrimSpace(text) == "yes":
pubkey = key.Marshal()
return nil
case strings.TrimSpace(text) == "no":
return errors.New("users says no")
default:
fmt.Println("Please answer 'yes' or 'no'")
continue
}
}
}
// If a public key exists for this SSH server, check that it matches

View File

@@ -131,13 +131,22 @@
"builtin": {
"name": "alt_bn128_add",
"pricing": {
"alt_bn128_const_operations": {
"price": 500,
"eip1108_transition_price": 150
"0x0": {
"price": {
"alt_bn128_const_operations": {
"price": 500
}
}
},
"0xc350": {
"price": {
"alt_bn128_const_operations": {
"price": 150
}
}
}
},
"activate_at": "0x7530",
"eip1108_transition": "0xc350"
"activate_at": "0x7530"
}
},
"0000000000000000000000000000000000000007": {
@@ -145,13 +154,22 @@
"builtin": {
"name": "alt_bn128_mul",
"pricing": {
"alt_bn128_const_operations": {
"price": 40000,
"eip1108_transition_price": 6000
"0x0": {
"price": {
"alt_bn128_const_operations": {
"price": 40000
}
}
},
"0xc350": {
"price": {
"alt_bn128_const_operations": {
"price": 6000
}
}
}
},
"activate_at": "0x7530",
"eip1108_transition": "0xc350"
"activate_at": "0x7530"
}
},
"0000000000000000000000000000000000000008": {
@@ -159,15 +177,24 @@
"builtin": {
"name": "alt_bn128_pairing",
"pricing": {
"alt_bn128_pairing": {
"base": 100000,
"pair": 80000,
"eip1108_transition_base": 45000,
"eip1108_transition_pair": 34000
"0x0": {
"price": {
"alt_bn128_pairing": {
"base": 100000,
"pair": 80000
}
}
},
"0xc350": {
"price": {
"alt_bn128_pairing": {
"base": 45000,
"pair": 34000
}
}
}
},
"activate_at": "0x7530",
"eip1108_transition": "0xc350"
"activate_at": "0x7530"
}
},
"0000000000000000000000000000000000000009": {

View File

@@ -185,28 +185,6 @@ func GlobalBig(ctx *cli.Context, name string) *big.Int {
return (*big.Int)(val.(*bigValue))
}
func prefixFor(name string) (prefix string) {
if len(name) == 1 {
prefix = "-"
} else {
prefix = "--"
}
return
}
func prefixedNames(fullName string) (prefixed string) {
parts := strings.Split(fullName, ",")
for i, name := range parts {
name = strings.Trim(name, " ")
prefixed += prefixFor(name) + name
if i < len(parts)-1 {
prefixed += ", "
}
}
return
}
// Expands a file path
// 1. replace tilde with users home dir
// 2. expands embedded environment variables

View File

@@ -42,7 +42,6 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/dashboard"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
@@ -78,6 +77,17 @@ SUBCOMMANDS:
{{range $categorized.Flags}}{{"\t"}}{{.}}
{{end}}
{{end}}{{end}}`
OriginCommandHelpTemplate = `{{.Name}}{{if .Subcommands}} command{{end}}{{if .Flags}} [command options]{{end}} [arguments...]
{{if .Description}}{{.Description}}
{{end}}{{if .Subcommands}}
SUBCOMMANDS:
{{range .Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .Flags}}
OPTIONS:
{{range $.Flags}}{{"\t"}}{{.}}
{{end}}
{{end}}`
)
func init() {
@@ -227,6 +237,10 @@ var (
Name: "override.istanbul",
Usage: "Manually specify Istanbul fork-block, overriding the bundled setting",
}
OverrideMuirGlacierFlag = cli.Uint64Flag{
Name: "override.muirglacier",
Usage: "Manually specify Muir Glacier fork-block, overriding the bundled setting",
}
// Light server and client settings
LightLegacyServFlag = cli.IntFlag{ // Deprecated in favor of light.serve, remove in 2021
Name: "lightserv",
@@ -272,26 +286,6 @@ var (
Name: "ulc.onlyannounce",
Usage: "Ultra light server sends announcements only",
}
// Dashboard settings
DashboardEnabledFlag = cli.BoolFlag{
Name: "dashboard",
Usage: "Enable the dashboard",
}
DashboardAddrFlag = cli.StringFlag{
Name: "dashboard.addr",
Usage: "Dashboard listening interface",
Value: dashboard.DefaultConfig.Host,
}
DashboardPortFlag = cli.IntFlag{
Name: "dashboard.host",
Usage: "Dashboard listening port",
Value: dashboard.DefaultConfig.Port,
}
DashboardRefreshFlag = cli.DurationFlag{
Name: "dashboard.refresh",
Usage: "Dashboard metrics collection refresh rate",
Value: dashboard.DefaultConfig.Refresh,
}
// Ethash settings
EthashCacheDirFlag = DirectoryFlag{
Name: "ethash.cachedir",
@@ -664,6 +658,10 @@ var (
Name: "netrestrict",
Usage: "Restricts network communication to the given IP networks (CIDR masks)",
}
DNSDiscoveryFlag = cli.StringFlag{
Name: "discovery.dns",
Usage: "Sets DNS discovery entry points (use \"\" to disable DNS)",
}
// ATM the url is left to the user and deployment to
JSpathFlag = cli.StringFlag{
@@ -817,9 +815,9 @@ func setBootstrapNodes(ctx *cli.Context, cfg *p2p.Config) {
switch {
case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV4Flag.Name):
if ctx.GlobalIsSet(BootnodesV4Flag.Name) {
urls = strings.Split(ctx.GlobalString(BootnodesV4Flag.Name), ",")
urls = splitAndTrim(ctx.GlobalString(BootnodesV4Flag.Name))
} else {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
urls = splitAndTrim(ctx.GlobalString(BootnodesFlag.Name))
}
case ctx.GlobalBool(TestnetFlag.Name):
urls = params.TestnetBootnodes
@@ -851,9 +849,9 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
switch {
case ctx.GlobalIsSet(BootnodesFlag.Name) || ctx.GlobalIsSet(BootnodesV5Flag.Name):
if ctx.GlobalIsSet(BootnodesV5Flag.Name) {
urls = strings.Split(ctx.GlobalString(BootnodesV5Flag.Name), ",")
urls = splitAndTrim(ctx.GlobalString(BootnodesV5Flag.Name))
} else {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
urls = splitAndTrim(ctx.GlobalString(BootnodesFlag.Name))
}
case ctx.GlobalBool(RinkebyFlag.Name):
urls = params.RinkebyBootnodes
@@ -1483,6 +1481,14 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
if ctx.GlobalIsSet(RPCGlobalGasCap.Name) {
cfg.RPCGasCap = new(big.Int).SetUint64(ctx.GlobalUint64(RPCGlobalGasCap.Name))
}
if ctx.GlobalIsSet(DNSDiscoveryFlag.Name) {
urls := ctx.GlobalString(DNSDiscoveryFlag.Name)
if urls == "" {
cfg.DiscoveryURLs = []string{}
} else {
cfg.DiscoveryURLs = splitAndTrim(urls)
}
}
// Override any default configs for hard coded networks.
switch {
@@ -1491,16 +1497,19 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.NetworkId = 3
}
cfg.Genesis = core.DefaultTestnetGenesisBlock()
setDNSDiscoveryDefaults(cfg, params.KnownDNSNetworks[params.TestnetGenesisHash])
case ctx.GlobalBool(RinkebyFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 4
}
cfg.Genesis = core.DefaultRinkebyGenesisBlock()
setDNSDiscoveryDefaults(cfg, params.KnownDNSNetworks[params.RinkebyGenesisHash])
case ctx.GlobalBool(GoerliFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 5
}
cfg.Genesis = core.DefaultGoerliGenesisBlock()
setDNSDiscoveryDefaults(cfg, params.KnownDNSNetworks[params.GoerliGenesisHash])
case ctx.GlobalBool(DeveloperFlag.Name):
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
cfg.NetworkId = 1337
@@ -1527,14 +1536,20 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) && !ctx.GlobalIsSet(MinerLegacyGasPriceFlag.Name) {
cfg.Miner.GasPrice = big.NewInt(1)
}
default:
if cfg.NetworkId == 1 {
setDNSDiscoveryDefaults(cfg, params.KnownDNSNetworks[params.MainnetGenesisHash])
}
}
}
// SetDashboardConfig applies dashboard related command line flags to the config.
func SetDashboardConfig(ctx *cli.Context, cfg *dashboard.Config) {
cfg.Host = ctx.GlobalString(DashboardAddrFlag.Name)
cfg.Port = ctx.GlobalInt(DashboardPortFlag.Name)
cfg.Refresh = ctx.GlobalDuration(DashboardRefreshFlag.Name)
// setDNSDiscoveryDefaults configures DNS discovery with the given URL if
// no URLs are set.
func setDNSDiscoveryDefaults(cfg *eth.Config, url string) {
if cfg.DiscoveryURLs != nil {
return
}
cfg.DiscoveryURLs = []string{url}
}
// RegisterEthService adds an Ethereum client to the stack.
@@ -1559,13 +1574,6 @@ func RegisterEthService(stack *node.Node, cfg *eth.Config) {
}
}
// RegisterDashboardService adds a dashboard to the stack.
func RegisterDashboardService(stack *node.Node, cfg *dashboard.Config, commit string) {
stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return dashboard.New(cfg, commit, ctx.ResolvePath("logs")), nil
})
}
// RegisterShhService configures Whisper and adds it to the given node.
func RegisterShhService(stack *node.Node, cfg *whisper.Config) {
if err := stack.Register(func(n *node.ServiceContext) (node.Service, error) {

View File

@@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/console"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
@@ -165,7 +166,7 @@ func echo() {
fmt.Printf("pow = %f \n", *argPoW)
fmt.Printf("mspow = %f \n", *argServerPoW)
fmt.Printf("ip = %s \n", *argIP)
fmt.Printf("pub = %s \n", common.ToHex(crypto.FromECDSAPub(pub)))
fmt.Printf("pub = %s \n", hexutil.Encode(crypto.FromECDSAPub(pub)))
fmt.Printf("idfile = %s \n", *argIDFile)
fmt.Printf("dbpath = %s \n", *argDBPath)
fmt.Printf("boot = %s \n", *argEnode)
@@ -298,7 +299,7 @@ func startServer() error {
return err
}
fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey)))
fmt.Printf("my public key: %s \n", hexutil.Encode(crypto.FromECDSAPub(&asymKey.PublicKey)))
fmt.Println(server.NodeInfo().Enode)
if *bootstrapMode {

View File

@@ -19,41 +19,43 @@ package common
import (
"bytes"
"testing"
checker "gopkg.in/check.v1"
)
type BytesSuite struct{}
func TestCopyBytes(t *testing.T) {
input := []byte{1, 2, 3, 4}
var _ = checker.Suite(&BytesSuite{})
func (s *BytesSuite) TestCopyBytes(c *checker.C) {
data1 := []byte{1, 2, 3, 4}
exp1 := []byte{1, 2, 3, 4}
res1 := CopyBytes(data1)
c.Assert(res1, checker.DeepEquals, exp1)
v := CopyBytes(input)
if !bytes.Equal(v, []byte{1, 2, 3, 4}) {
t.Fatal("not equal after copy")
}
v[0] = 99
if bytes.Equal(v, input) {
t.Fatal("result is not a copy")
}
}
func (s *BytesSuite) TestLeftPadBytes(c *checker.C) {
val1 := []byte{1, 2, 3, 4}
exp1 := []byte{0, 0, 0, 0, 1, 2, 3, 4}
res1 := LeftPadBytes(val1, 8)
res2 := LeftPadBytes(val1, 2)
c.Assert(res1, checker.DeepEquals, exp1)
c.Assert(res2, checker.DeepEquals, val1)
}
func (s *BytesSuite) TestRightPadBytes(c *checker.C) {
func TestLeftPadBytes(t *testing.T) {
val := []byte{1, 2, 3, 4}
exp := []byte{1, 2, 3, 4, 0, 0, 0, 0}
padded := []byte{0, 0, 0, 0, 1, 2, 3, 4}
resstd := RightPadBytes(val, 8)
resshrt := RightPadBytes(val, 2)
if r := LeftPadBytes(val, 8); !bytes.Equal(r, padded) {
t.Fatalf("LeftPadBytes(%v, 8) == %v", val, r)
}
if r := LeftPadBytes(val, 2); !bytes.Equal(r, val) {
t.Fatalf("LeftPadBytes(%v, 2) == %v", val, r)
}
}
c.Assert(resstd, checker.DeepEquals, exp)
c.Assert(resshrt, checker.DeepEquals, val)
func TestRightPadBytes(t *testing.T) {
val := []byte{1, 2, 3, 4}
padded := []byte{1, 2, 3, 4, 0, 0, 0, 0}
if r := RightPadBytes(val, 8); !bytes.Equal(r, padded) {
t.Fatalf("RightPadBytes(%v, 8) == %v", val, r)
}
if r := RightPadBytes(val, 2); !bytes.Equal(r, val) {
t.Fatalf("RightPadBytes(%v, 2) == %v", val, r)
}
}
func TestFromHex(t *testing.T) {

View File

@@ -86,7 +86,7 @@ func (b *Bytes) UnmarshalGraphQL(input interface{}) error {
}
*b = data
default:
err = fmt.Errorf("Unexpected type for Bytes: %v", input)
err = fmt.Errorf("unexpected type %T for Bytes", input)
}
return err
}
@@ -220,7 +220,7 @@ func (b *Big) UnmarshalGraphQL(input interface{}) error {
num.SetInt64(int64(input))
*b = Big(num)
default:
err = fmt.Errorf("Unexpected type for BigInt: %v", input)
err = fmt.Errorf("unexpected type %T for BigInt", input)
}
return err
}
@@ -284,7 +284,7 @@ func (b *Uint64) UnmarshalGraphQL(input interface{}) error {
case int32:
*b = Uint64(input)
default:
err = fmt.Errorf("Unexpected type for Long: %v", input)
err = fmt.Errorf("unexpected type %T for Long", input)
}
return err
}

View File

@@ -1,25 +0,0 @@
// Copyright 2014 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 common
import (
"testing"
checker "gopkg.in/check.v1"
)
func Test(t *testing.T) { checker.TestingT(t) }

View File

@@ -31,44 +31,93 @@ func Now() AbsTime {
return AbsTime(monotime.Now())
}
// Add returns t + d.
// Add returns t + d as absolute time.
func (t AbsTime) Add(d time.Duration) AbsTime {
return t + AbsTime(d)
}
// Sub returns t - t2 as a duration.
func (t AbsTime) Sub(t2 AbsTime) time.Duration {
return time.Duration(t - t2)
}
// The Clock interface makes it possible to replace the monotonic system clock with
// a simulated clock.
type Clock interface {
Now() AbsTime
Sleep(time.Duration)
After(time.Duration) <-chan time.Time
NewTimer(time.Duration) ChanTimer
After(time.Duration) <-chan AbsTime
AfterFunc(d time.Duration, f func()) Timer
}
// Timer represents a cancellable event returned by AfterFunc
// Timer is a cancellable event created by AfterFunc.
type Timer interface {
// Stop cancels the timer. It returns false if the timer has already
// expired or been stopped.
Stop() bool
}
// ChanTimer is a cancellable event created by NewTimer.
type ChanTimer interface {
Timer
// The channel returned by C receives a value when the timer expires.
C() <-chan AbsTime
// Reset reschedules the timer with a new timeout.
// It should be invoked only on stopped or expired timers with drained channels.
Reset(time.Duration)
}
// System implements Clock using the system clock.
type System struct{}
// Now returns the current monotonic time.
func (System) Now() AbsTime {
func (c System) Now() AbsTime {
return AbsTime(monotime.Now())
}
// Sleep blocks for the given duration.
func (System) Sleep(d time.Duration) {
func (c System) Sleep(d time.Duration) {
time.Sleep(d)
}
// NewTimer creates a timer which can be rescheduled.
func (c System) NewTimer(d time.Duration) ChanTimer {
ch := make(chan AbsTime, 1)
t := time.AfterFunc(d, func() {
// This send is non-blocking because that's how time.Timer
// behaves. It doesn't matter in the happy case, but does
// when Reset is misused.
select {
case ch <- c.Now():
default:
}
})
return &systemTimer{t, ch}
}
// After returns a channel which receives the current time after d has elapsed.
func (System) After(d time.Duration) <-chan time.Time {
return time.After(d)
func (c System) After(d time.Duration) <-chan AbsTime {
ch := make(chan AbsTime, 1)
time.AfterFunc(d, func() { ch <- c.Now() })
return ch
}
// AfterFunc runs f on a new goroutine after the duration has elapsed.
func (System) AfterFunc(d time.Duration, f func()) Timer {
func (c System) AfterFunc(d time.Duration, f func()) Timer {
return time.AfterFunc(d, f)
}
type systemTimer struct {
*time.Timer
ch <-chan AbsTime
}
func (st *systemTimer) Reset(d time.Duration) {
st.Timer.Reset(d)
}
func (st *systemTimer) C() <-chan AbsTime {
return st.ch
}

View File

@@ -17,6 +17,7 @@
package mclock
import (
"container/heap"
"sync"
"time"
)
@@ -32,18 +33,24 @@ import (
// the timeout using a channel or semaphore.
type Simulated struct {
now AbsTime
scheduled []*simTimer
scheduled simTimerHeap
mu sync.RWMutex
cond *sync.Cond
lastId uint64
}
// simTimer implements Timer on the virtual clock.
// simTimer implements ChanTimer on the virtual clock.
type simTimer struct {
do func()
at AbsTime
id uint64
s *Simulated
at AbsTime
index int // position in s.scheduled
s *Simulated
do func()
ch <-chan AbsTime
}
func (s *Simulated) init() {
if s.cond == nil {
s.cond = sync.NewCond(&s.mu)
}
}
// Run moves the clock by the given duration, executing all timers before that duration.
@@ -53,14 +60,9 @@ func (s *Simulated) Run(d time.Duration) {
end := s.now + AbsTime(d)
var do []func()
for len(s.scheduled) > 0 {
ev := s.scheduled[0]
if ev.at > end {
break
}
s.now = ev.at
for len(s.scheduled) > 0 && s.scheduled[0].at <= end {
ev := heap.Pop(&s.scheduled).(*simTimer)
do = append(do, ev.do)
s.scheduled = s.scheduled[1:]
}
s.now = end
s.mu.Unlock()
@@ -102,14 +104,22 @@ func (s *Simulated) Sleep(d time.Duration) {
<-s.After(d)
}
// NewTimer creates a timer which fires when the clock has advanced by d.
func (s *Simulated) NewTimer(d time.Duration) ChanTimer {
s.mu.Lock()
defer s.mu.Unlock()
ch := make(chan AbsTime, 1)
var timer *simTimer
timer = s.schedule(d, func() { ch <- timer.at })
timer.ch = ch
return timer
}
// After returns a channel which receives the current time after the clock
// has advanced by d.
func (s *Simulated) After(d time.Duration) <-chan time.Time {
after := make(chan time.Time, 1)
s.AfterFunc(d, func() {
after <- (time.Time{}).Add(time.Duration(s.now))
})
return after
func (s *Simulated) After(d time.Duration) <-chan AbsTime {
return s.NewTimer(d).C()
}
// AfterFunc runs fn after the clock has advanced by d. Unlike with the system
@@ -117,46 +127,83 @@ func (s *Simulated) After(d time.Duration) <-chan time.Time {
func (s *Simulated) AfterFunc(d time.Duration, fn func()) Timer {
s.mu.Lock()
defer s.mu.Unlock()
return s.schedule(d, fn)
}
func (s *Simulated) schedule(d time.Duration, fn func()) *simTimer {
s.init()
at := s.now + AbsTime(d)
s.lastId++
id := s.lastId
l, h := 0, len(s.scheduled)
ll := h
for l != h {
m := (l + h) / 2
if (at < s.scheduled[m].at) || ((at == s.scheduled[m].at) && (id < s.scheduled[m].id)) {
h = m
} else {
l = m + 1
}
}
ev := &simTimer{do: fn, at: at, s: s}
s.scheduled = append(s.scheduled, nil)
copy(s.scheduled[l+1:], s.scheduled[l:ll])
s.scheduled[l] = ev
heap.Push(&s.scheduled, ev)
s.cond.Broadcast()
return ev
}
func (ev *simTimer) Stop() bool {
s := ev.s
s.mu.Lock()
defer s.mu.Unlock()
ev.s.mu.Lock()
defer ev.s.mu.Unlock()
for i := 0; i < len(s.scheduled); i++ {
if s.scheduled[i] == ev {
s.scheduled = append(s.scheduled[:i], s.scheduled[i+1:]...)
s.cond.Broadcast()
return true
}
if ev.index < 0 {
return false
}
return false
heap.Remove(&ev.s.scheduled, ev.index)
ev.s.cond.Broadcast()
ev.index = -1
return true
}
func (s *Simulated) init() {
if s.cond == nil {
s.cond = sync.NewCond(&s.mu)
func (ev *simTimer) Reset(d time.Duration) {
if ev.ch == nil {
panic("mclock: Reset() on timer created by AfterFunc")
}
ev.s.mu.Lock()
defer ev.s.mu.Unlock()
ev.at = ev.s.now.Add(d)
if ev.index < 0 {
heap.Push(&ev.s.scheduled, ev) // already expired
} else {
heap.Fix(&ev.s.scheduled, ev.index) // hasn't fired yet, reschedule
}
ev.s.cond.Broadcast()
}
func (ev *simTimer) C() <-chan AbsTime {
if ev.ch == nil {
panic("mclock: C() on timer created by AfterFunc")
}
return ev.ch
}
type simTimerHeap []*simTimer
func (h *simTimerHeap) Len() int {
return len(*h)
}
func (h *simTimerHeap) Less(i, j int) bool {
return (*h)[i].at < (*h)[j].at
}
func (h *simTimerHeap) Swap(i, j int) {
(*h)[i], (*h)[j] = (*h)[j], (*h)[i]
(*h)[i].index = i
(*h)[j].index = j
}
func (h *simTimerHeap) Push(x interface{}) {
t := x.(*simTimer)
t.index = len(*h)
*h = append(*h, t)
}
func (h *simTimerHeap) Pop() interface{} {
end := len(*h) - 1
t := (*h)[end]
t.index = -1
(*h)[end] = nil
*h = (*h)[:end]
return t
}

View File

@@ -25,14 +25,16 @@ var _ Clock = System{}
var _ Clock = new(Simulated)
func TestSimulatedAfter(t *testing.T) {
const timeout = 30 * time.Minute
const adv = time.Minute
var (
c Simulated
end = c.Now().Add(timeout)
ch = c.After(timeout)
timeout = 30 * time.Minute
offset = 99 * time.Hour
adv = 11 * time.Minute
c Simulated
)
c.Run(offset)
end := c.Now().Add(timeout)
ch := c.After(timeout)
for c.Now() < end.Add(-adv) {
c.Run(adv)
select {
@@ -45,8 +47,8 @@ func TestSimulatedAfter(t *testing.T) {
c.Run(adv)
select {
case stamp := <-ch:
want := time.Time{}.Add(timeout)
if !stamp.Equal(want) {
want := AbsTime(0).Add(offset).Add(timeout)
if stamp != want {
t.Errorf("Wrong time sent on timer channel: got %v, want %v", stamp, want)
}
default:
@@ -94,7 +96,7 @@ func TestSimulatedSleep(t *testing.T) {
var (
c Simulated
timeout = 1 * time.Hour
done = make(chan AbsTime)
done = make(chan AbsTime, 1)
)
go func() {
c.Sleep(timeout)
@@ -113,3 +115,48 @@ func TestSimulatedSleep(t *testing.T) {
t.Fatal("Sleep didn't return in time")
}
}
func TestSimulatedTimerReset(t *testing.T) {
var (
c Simulated
timeout = 1 * time.Hour
)
timer := c.NewTimer(timeout)
c.Run(2 * timeout)
select {
case ftime := <-timer.C():
if ftime != AbsTime(timeout) {
t.Fatalf("wrong time %v sent on timer channel, want %v", ftime, AbsTime(timeout))
}
default:
t.Fatal("timer didn't fire")
}
timer.Reset(timeout)
c.Run(2 * timeout)
select {
case ftime := <-timer.C():
if ftime != AbsTime(3*timeout) {
t.Fatalf("wrong time %v sent on timer channel, want %v", ftime, AbsTime(3*timeout))
}
default:
t.Fatal("timer didn't fire again")
}
}
func TestSimulatedTimerStop(t *testing.T) {
var (
c Simulated
timeout = 1 * time.Hour
)
timer := c.NewTimer(timeout)
c.Run(2 * timeout)
if timer.Stop() {
t.Errorf("Stop returned true for fired timer")
}
select {
case <-timer.C():
default:
t.Fatal("timer didn't fire")
}
}

View File

@@ -20,6 +20,7 @@ import (
"database/sql/driver"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"math/big"
"math/rand"
@@ -142,7 +143,7 @@ func (h Hash) Value() (driver.Value, error) {
}
// ImplementsGraphQLType returns true if Hash implements the specified GraphQL type.
func (_ Hash) ImplementsGraphQLType(name string) bool { return name == "Bytes32" }
func (Hash) ImplementsGraphQLType(name string) bool { return name == "Bytes32" }
// UnmarshalGraphQL unmarshals the provided GraphQL query data.
func (h *Hash) UnmarshalGraphQL(input interface{}) error {
@@ -151,7 +152,7 @@ func (h *Hash) UnmarshalGraphQL(input interface{}) error {
case string:
err = h.UnmarshalText([]byte(input))
default:
err = fmt.Errorf("Unexpected type for Bytes32: %v", input)
err = fmt.Errorf("unexpected type %T for Hash", input)
}
return err
}
@@ -290,7 +291,7 @@ func (a *Address) UnmarshalGraphQL(input interface{}) error {
case string:
err = a.UnmarshalText([]byte(input))
default:
err = fmt.Errorf("Unexpected type for Address: %v", input)
err = fmt.Errorf("unexpected type %T for Address", input)
}
return err
}
@@ -323,7 +324,7 @@ func NewMixedcaseAddress(addr Address) MixedcaseAddress {
// NewMixedcaseAddressFromString is mainly meant for unit-testing
func NewMixedcaseAddressFromString(hexaddr string) (*MixedcaseAddress, error) {
if !IsHexAddress(hexaddr) {
return nil, fmt.Errorf("Invalid address")
return nil, errors.New("invalid address")
}
a := FromHex(hexaddr)
return &MixedcaseAddress{addr: BytesToAddress(a), original: hexaddr}, nil

View File

@@ -17,6 +17,8 @@
package clique
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
@@ -117,3 +119,59 @@ func (api *API) Discard(address common.Address) {
delete(api.clique.proposals, address)
}
type status struct {
InturnPercent float64 `json:"inturnPercent"`
SigningStatus map[common.Address]int `json:"sealerActivity"`
NumBlocks uint64 `json:"numBlocks"`
}
// Status returns the status of the last N blocks,
// - the number of active signers,
// - the number of signers,
// - the percentage of in-turn blocks
func (api *API) Status() (*status, error) {
var (
numBlocks = uint64(64)
header = api.chain.CurrentHeader()
diff = uint64(0)
optimals = 0
)
snap, err := api.clique.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
if err != nil {
return nil, err
}
var (
signers = snap.signers()
end = header.Number.Uint64()
start = end - numBlocks
)
if numBlocks > end {
start = 1
numBlocks = end - start
}
signStatus := make(map[common.Address]int)
for _, s := range signers {
signStatus[s] = 0
}
for n := start; n < end; n++ {
h := api.chain.GetHeaderByNumber(n)
if h == nil {
return nil, fmt.Errorf("missing block %d", n)
}
if h.Difficulty.Cmp(diffInTurn) == 0 {
optimals++
}
diff += h.Difficulty.Uint64()
sealer, err := api.clique.Author(h)
if err != nil {
return nil, err
}
signStatus[sealer]++
}
return &status{
InturnPercent: float64((100 * optimals)) / float64(numBlocks),
SigningStatus: signStatus,
NumBlocks: numBlocks,
}, nil
}

View File

@@ -729,7 +729,7 @@ func TestConcurrentDiskCacheGeneration(t *testing.T) {
go func(idx int) {
defer pend.Done()
ethash := New(Config{cachedir, 0, 1, "", 0, 0, ModeNormal}, nil, false)
ethash := New(Config{cachedir, 0, 1, "", 0, 0, ModeNormal, nil}, nil, false)
defer ethash.Close()
if err := ethash.VerifySeal(nil, block.Header()); err != nil {
t.Errorf("proc %d: block verification failed: %v", idx, err)

View File

@@ -28,7 +28,7 @@ var errEthashStopped = errors.New("ethash stopped")
// API exposes ethash related methods for the RPC interface.
type API struct {
ethash *Ethash // Make sure the mode of ethash is normal.
ethash *Ethash
}
// GetWork returns a work package for external miner.
@@ -39,7 +39,7 @@ type API struct {
// result[2] - 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
// result[3] - hex encoded block number
func (api *API) GetWork() ([4]string, error) {
if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest {
if api.ethash.remote == nil {
return [4]string{}, errors.New("not supported")
}
@@ -47,13 +47,11 @@ func (api *API) GetWork() ([4]string, error) {
workCh = make(chan [4]string, 1)
errc = make(chan error, 1)
)
select {
case api.ethash.fetchWorkCh <- &sealWork{errc: errc, res: workCh}:
case <-api.ethash.exitCh:
case api.ethash.remote.fetchWorkCh <- &sealWork{errc: errc, res: workCh}:
case <-api.ethash.remote.exitCh:
return [4]string{}, errEthashStopped
}
select {
case work := <-workCh:
return work, nil
@@ -66,23 +64,21 @@ func (api *API) GetWork() ([4]string, error) {
// It returns an indication if the work was accepted.
// Note either an invalid solution, a stale work a non-existent work will return false.
func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) bool {
if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest {
if api.ethash.remote == nil {
return false
}
var errc = make(chan error, 1)
select {
case api.ethash.submitWorkCh <- &mineResult{
case api.ethash.remote.submitWorkCh <- &mineResult{
nonce: nonce,
mixDigest: digest,
hash: hash,
errc: errc,
}:
case <-api.ethash.exitCh:
case <-api.ethash.remote.exitCh:
return false
}
err := <-errc
return err == nil
}
@@ -94,21 +90,19 @@ func (api *API) SubmitWork(nonce types.BlockNonce, hash, digest common.Hash) boo
// It accepts the miner hash rate and an identifier which must be unique
// between nodes.
func (api *API) SubmitHashRate(rate hexutil.Uint64, id common.Hash) bool {
if api.ethash.config.PowMode != ModeNormal && api.ethash.config.PowMode != ModeTest {
if api.ethash.remote == nil {
return false
}
var done = make(chan struct{}, 1)
select {
case api.ethash.submitRateCh <- &hashrate{done: done, rate: uint64(rate), id: id}:
case <-api.ethash.exitCh:
case api.ethash.remote.submitRateCh <- &hashrate{done: done, rate: uint64(rate), id: id}:
case <-api.ethash.remote.exitCh:
return false
}
// Block until hash rate submitted successfully.
<-done
return true
}

View File

@@ -44,6 +44,11 @@ var (
maxUncles = 2 // Maximum number of uncles allowed in a single block
allowedFutureBlockTime = 15 * time.Second // Max time from current time allowed for blocks, before they're considered future blocks
// calcDifficultyEip2384 is the difficulty adjustment algorithm as specified by EIP 2384.
// It offsets the bomb 4M blocks from Constantinople, so in total 9M blocks.
// Specification EIP-2384: https://eips.ethereum.org/EIPS/eip-2384
calcDifficultyEip2384 = makeDifficultyCalculator(big.NewInt(9000000))
// calcDifficultyConstantinople is the difficulty adjustment algorithm for Constantinople.
// It returns the difficulty that a new block should have when created at time given the
// parent block's time and difficulty. The calculation uses the Byzantium rules, but with
@@ -63,7 +68,7 @@ var (
// codebase, inherently breaking if the engine is swapped out. Please put common
// error types into the consensus package.
var (
errZeroBlockTime = errors.New("timestamp equals parent's")
errOlderBlockTime = errors.New("timestamp older than parent")
errTooManyUncles = errors.New("too many uncles")
errDuplicateUncle = errors.New("duplicate uncle")
errUncleIsAncestor = errors.New("uncle is ancestor")
@@ -250,9 +255,9 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
}
}
if header.Time <= parent.Time {
return errZeroBlockTime
return errOlderBlockTime
}
// Verify the block's difficulty based in its timestamp and parent's difficulty
// Verify the block's difficulty based on its timestamp and parent's difficulty
expected := ethash.CalcDifficulty(chain, header.Time, parent)
if expected.Cmp(header.Difficulty) != 0 {
@@ -311,6 +316,8 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, p
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
next := new(big.Int).Add(parent.Number, big1)
switch {
case config.IsMuirGlacier(next):
return calcDifficultyEip2384(time, parent)
case config.IsConstantinople(next):
return calcDifficultyConstantinople(time, parent)
case config.IsByzantium(next):

View File

@@ -34,9 +34,7 @@ import (
"unsafe"
mmap "github.com/edsrzf/mmap-go"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rpc"
@@ -50,7 +48,7 @@ var (
two256 = new(big.Int).Exp(big.NewInt(2), big.NewInt(256), big.NewInt(0))
// sharedEthash is a full instance that can be shared between multiple users.
sharedEthash = New(Config{"", 3, 0, "", 1, 0, ModeNormal}, nil, false)
sharedEthash = New(Config{"", 3, 0, "", 1, 0, ModeNormal, nil}, nil, false)
// algorithmRevision is the data structure version used for file naming.
algorithmRevision = 23
@@ -403,36 +401,8 @@ type Config struct {
DatasetsInMem int
DatasetsOnDisk int
PowMode Mode
}
// sealTask wraps a seal block with relative result channel for remote sealer thread.
type sealTask struct {
block *types.Block
results chan<- *types.Block
}
// mineResult wraps the pow solution parameters for the specified block.
type mineResult struct {
nonce types.BlockNonce
mixDigest common.Hash
hash common.Hash
errc chan error
}
// hashrate wraps the hash rate submitted by the remote sealer.
type hashrate struct {
id common.Hash
ping time.Time
rate uint64
done chan struct{}
}
// sealWork wraps a seal work package for remote sealer.
type sealWork struct {
errc chan error
res chan [4]string
Log log.Logger `toml:"-"`
}
// Ethash is a consensus engine based on proof-of-work implementing the ethash
@@ -448,52 +418,42 @@ type Ethash struct {
threads int // Number of threads to mine on if mining
update chan struct{} // Notification channel to update mining parameters
hashrate metrics.Meter // Meter tracking the average hashrate
// Remote sealer related fields
workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer
fetchWorkCh chan *sealWork // Channel used for remote sealer to fetch mining work
submitWorkCh chan *mineResult // Channel used for remote sealer to submit their mining result
fetchRateCh chan chan uint64 // Channel used to gather submitted hash rate for local or remote sealer.
submitRateCh chan *hashrate // Channel used for remote sealer to submit their mining hashrate
remote *remoteSealer
// The fields below are hooks for testing
shared *Ethash // Shared PoW verifier to avoid cache regeneration
fakeFail uint64 // Block number which fails PoW check even in fake mode
fakeDelay time.Duration // Time delay to sleep for before returning from verify
lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
closeOnce sync.Once // Ensures exit channel will not be closed twice.
exitCh chan chan error // Notification channel to exiting backend threads
lock sync.Mutex // Ensures thread safety for the in-memory caches and mining fields
closeOnce sync.Once // Ensures exit channel will not be closed twice.
}
// New creates a full sized ethash PoW scheme and starts a background thread for
// remote mining, also optionally notifying a batch of remote services of new work
// packages.
func New(config Config, notify []string, noverify bool) *Ethash {
if config.Log == nil {
config.Log = log.Root()
}
if config.CachesInMem <= 0 {
log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
config.Log.Warn("One ethash cache must always be in memory", "requested", config.CachesInMem)
config.CachesInMem = 1
}
if config.CacheDir != "" && config.CachesOnDisk > 0 {
log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
config.Log.Info("Disk storage enabled for ethash caches", "dir", config.CacheDir, "count", config.CachesOnDisk)
}
if config.DatasetDir != "" && config.DatasetsOnDisk > 0 {
log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
config.Log.Info("Disk storage enabled for ethash DAGs", "dir", config.DatasetDir, "count", config.DatasetsOnDisk)
}
ethash := &Ethash{
config: config,
caches: newlru("cache", config.CachesInMem, newCache),
datasets: newlru("dataset", config.DatasetsInMem, newDataset),
update: make(chan struct{}),
hashrate: metrics.NewMeterForced(),
workCh: make(chan *sealTask),
fetchWorkCh: make(chan *sealWork),
submitWorkCh: make(chan *mineResult),
fetchRateCh: make(chan chan uint64),
submitRateCh: make(chan *hashrate),
exitCh: make(chan chan error),
config: config,
caches: newlru("cache", config.CachesInMem, newCache),
datasets: newlru("dataset", config.DatasetsInMem, newDataset),
update: make(chan struct{}),
hashrate: metrics.NewMeterForced(),
}
go ethash.remote(notify, noverify)
ethash.remote = startRemoteSealer(ethash, notify, noverify)
return ethash
}
@@ -501,19 +461,13 @@ func New(config Config, notify []string, noverify bool) *Ethash {
// purposes.
func NewTester(notify []string, noverify bool) *Ethash {
ethash := &Ethash{
config: Config{PowMode: ModeTest},
caches: newlru("cache", 1, newCache),
datasets: newlru("dataset", 1, newDataset),
update: make(chan struct{}),
hashrate: metrics.NewMeterForced(),
workCh: make(chan *sealTask),
fetchWorkCh: make(chan *sealWork),
submitWorkCh: make(chan *mineResult),
fetchRateCh: make(chan chan uint64),
submitRateCh: make(chan *hashrate),
exitCh: make(chan chan error),
config: Config{PowMode: ModeTest, Log: log.Root()},
caches: newlru("cache", 1, newCache),
datasets: newlru("dataset", 1, newDataset),
update: make(chan struct{}),
hashrate: metrics.NewMeterForced(),
}
go ethash.remote(notify, noverify)
ethash.remote = startRemoteSealer(ethash, notify, noverify)
return ethash
}
@@ -524,6 +478,7 @@ func NewFaker() *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFake,
Log: log.Root(),
},
}
}
@@ -535,6 +490,7 @@ func NewFakeFailer(fail uint64) *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFake,
Log: log.Root(),
},
fakeFail: fail,
}
@@ -547,6 +503,7 @@ func NewFakeDelayer(delay time.Duration) *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFake,
Log: log.Root(),
},
fakeDelay: delay,
}
@@ -558,6 +515,7 @@ func NewFullFaker() *Ethash {
return &Ethash{
config: Config{
PowMode: ModeFullFake,
Log: log.Root(),
},
}
}
@@ -573,13 +531,11 @@ func (ethash *Ethash) Close() error {
var err error
ethash.closeOnce.Do(func() {
// Short circuit if the exit channel is not allocated.
if ethash.exitCh == nil {
if ethash.remote == nil {
return
}
errc := make(chan error)
ethash.exitCh <- errc
err = <-errc
close(ethash.exitCh)
close(ethash.remote.requestExit)
<-ethash.remote.exitCh
})
return err
}
@@ -680,8 +636,8 @@ func (ethash *Ethash) Hashrate() float64 {
var res = make(chan uint64, 1)
select {
case ethash.fetchRateCh <- res:
case <-ethash.exitCh:
case ethash.remote.fetchRateCh <- res:
case <-ethash.remote.exitCh:
// Return local hashrate only if ethash is stopped.
return ethash.hashrate.Rate1()
}

View File

@@ -49,7 +49,7 @@ func TestTestMode(t *testing.T) {
if err := ethash.VerifySeal(nil, header); err != nil {
t.Fatalf("unexpected verification error: %v", err)
}
case <-time.NewTimer(time.Second).C:
case <-time.NewTimer(2 * time.Second).C:
t.Error("sealing result timeout")
}
}

View File

@@ -18,6 +18,7 @@ package ethash
import (
"bytes"
"context"
crand "crypto/rand"
"encoding/json"
"errors"
@@ -33,7 +34,6 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
)
const (
@@ -56,7 +56,7 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, resu
select {
case results <- block.WithSeal(header):
default:
log.Warn("Sealing result is not read by miner", "mode", "fake", "sealhash", ethash.SealHash(block.Header()))
ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "fake", "sealhash", ethash.SealHash(block.Header()))
}
return nil
}
@@ -85,8 +85,8 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, resu
threads = 0 // Allows disabling local mining without extra logic around local/remote
}
// Push new work to remote sealer
if ethash.workCh != nil {
ethash.workCh <- &sealTask{block: block, results: results}
if ethash.remote != nil {
ethash.remote.workCh <- &sealTask{block: block, results: results}
}
var (
pend sync.WaitGroup
@@ -111,14 +111,14 @@ func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, resu
select {
case results <- result:
default:
log.Warn("Sealing result is not read by miner", "mode", "local", "sealhash", ethash.SealHash(block.Header()))
ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "local", "sealhash", ethash.SealHash(block.Header()))
}
close(abort)
case <-ethash.update:
// Thread count was changed on user request, restart
close(abort)
if err := ethash.Seal(chain, block, results, stop); err != nil {
log.Error("Failed to restart sealing after update", "err", err)
ethash.config.Log.Error("Failed to restart sealing after update", "err", err)
}
}
// Wait for all miners to terminate and return the block
@@ -143,7 +143,7 @@ func (ethash *Ethash) mine(block *types.Block, id int, seed uint64, abort chan s
attempts = int64(0)
nonce = seed
)
logger := log.New("miner", id)
logger := ethash.config.Log.New("miner", id)
logger.Trace("Started ethash search for new nonces", "seed", seed)
search:
for {
@@ -186,160 +186,128 @@ search:
runtime.KeepAlive(dataset)
}
// remote is a standalone goroutine to handle remote mining related stuff.
func (ethash *Ethash) remote(notify []string, noverify bool) {
var (
works = make(map[common.Hash]*types.Block)
rates = make(map[common.Hash]hashrate)
// This is the timeout for HTTP requests to notify external miners.
const remoteSealerTimeout = 1 * time.Second
results chan<- *types.Block
currentBlock *types.Block
currentWork [4]string
type remoteSealer struct {
works map[common.Hash]*types.Block
rates map[common.Hash]hashrate
currentBlock *types.Block
currentWork [4]string
notifyCtx context.Context
cancelNotify context.CancelFunc // cancels all notification requests
reqWG sync.WaitGroup // tracks notification request goroutines
notifyTransport = &http.Transport{}
notifyClient = &http.Client{
Transport: notifyTransport,
Timeout: time.Second,
}
notifyReqs = make([]*http.Request, len(notify))
)
// notifyWork notifies all the specified mining endpoints of the availability of
// new work to be processed.
notifyWork := func() {
work := currentWork
blob, _ := json.Marshal(work)
ethash *Ethash
noverify bool
notifyURLs []string
results chan<- *types.Block
workCh chan *sealTask // Notification channel to push new work and relative result channel to remote sealer
fetchWorkCh chan *sealWork // Channel used for remote sealer to fetch mining work
submitWorkCh chan *mineResult // Channel used for remote sealer to submit their mining result
fetchRateCh chan chan uint64 // Channel used to gather submitted hash rate for local or remote sealer.
submitRateCh chan *hashrate // Channel used for remote sealer to submit their mining hashrate
requestExit chan struct{}
exitCh chan struct{}
}
for i, url := range notify {
// Terminate any previously pending request and create the new work
if notifyReqs[i] != nil {
notifyTransport.CancelRequest(notifyReqs[i])
}
notifyReqs[i], _ = http.NewRequest("POST", url, bytes.NewReader(blob))
notifyReqs[i].Header.Set("Content-Type", "application/json")
// sealTask wraps a seal block with relative result channel for remote sealer thread.
type sealTask struct {
block *types.Block
results chan<- *types.Block
}
// Push the new work concurrently to all the remote nodes
go func(req *http.Request, url string) {
res, err := notifyClient.Do(req)
if err != nil {
log.Warn("Failed to notify remote miner", "err", err)
} else {
log.Trace("Notified remote miner", "miner", url, "hash", log.Lazy{Fn: func() common.Hash { return common.HexToHash(work[0]) }}, "target", work[2])
res.Body.Close()
}
}(notifyReqs[i], url)
}
// mineResult wraps the pow solution parameters for the specified block.
type mineResult struct {
nonce types.BlockNonce
mixDigest common.Hash
hash common.Hash
errc chan error
}
// hashrate wraps the hash rate submitted by the remote sealer.
type hashrate struct {
id common.Hash
ping time.Time
rate uint64
done chan struct{}
}
// sealWork wraps a seal work package for remote sealer.
type sealWork struct {
errc chan error
res chan [4]string
}
func startRemoteSealer(ethash *Ethash, urls []string, noverify bool) *remoteSealer {
ctx, cancel := context.WithCancel(context.Background())
s := &remoteSealer{
ethash: ethash,
noverify: noverify,
notifyURLs: urls,
notifyCtx: ctx,
cancelNotify: cancel,
works: make(map[common.Hash]*types.Block),
rates: make(map[common.Hash]hashrate),
workCh: make(chan *sealTask),
fetchWorkCh: make(chan *sealWork),
submitWorkCh: make(chan *mineResult),
fetchRateCh: make(chan chan uint64),
submitRateCh: make(chan *hashrate),
requestExit: make(chan struct{}),
exitCh: make(chan struct{}),
}
// makeWork creates a work package for external miner.
//
// The work package consists of 3 strings:
// result[0], 32 bytes hex encoded current block header pow-hash
// result[1], 32 bytes hex encoded seed hash used for DAG
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
// result[3], hex encoded block number
makeWork := func(block *types.Block) {
hash := ethash.SealHash(block.Header())
go s.loop()
return s
}
currentWork[0] = hash.Hex()
currentWork[1] = common.BytesToHash(SeedHash(block.NumberU64())).Hex()
currentWork[2] = common.BytesToHash(new(big.Int).Div(two256, block.Difficulty()).Bytes()).Hex()
currentWork[3] = hexutil.EncodeBig(block.Number())
// Trace the seal work fetched by remote sealer.
currentBlock = block
works[hash] = block
}
// submitWork verifies the submitted pow solution, returning
// whether the solution was accepted or not (not can be both a bad pow as well as
// any other error, like no pending work or stale mining result).
submitWork := func(nonce types.BlockNonce, mixDigest common.Hash, sealhash common.Hash) bool {
if currentBlock == nil {
log.Error("Pending work without block", "sealhash", sealhash)
return false
}
// Make sure the work submitted is present
block := works[sealhash]
if block == nil {
log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", currentBlock.NumberU64())
return false
}
// Verify the correctness of submitted result.
header := block.Header()
header.Nonce = nonce
header.MixDigest = mixDigest
start := time.Now()
if !noverify {
if err := ethash.verifySeal(nil, header, true); err != nil {
log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err)
return false
}
}
// Make sure the result channel is assigned.
if results == nil {
log.Warn("Ethash result channel is empty, submitted mining result is rejected")
return false
}
log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)))
// Solutions seems to be valid, return to the miner and notify acceptance.
solution := block.WithSeal(header)
// The submitted solution is within the scope of acceptance.
if solution.NumberU64()+staleThreshold > currentBlock.NumberU64() {
select {
case results <- solution:
log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
return true
default:
log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash)
return false
}
}
// The submitted block is too old to accept, drop it.
log.Warn("Work submitted is too old", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
return false
}
func (s *remoteSealer) loop() {
defer func() {
s.ethash.config.Log.Trace("Ethash remote sealer is exiting")
s.cancelNotify()
s.reqWG.Wait()
close(s.exitCh)
}()
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case work := <-ethash.workCh:
case work := <-s.workCh:
// Update current work with new received block.
// Note same work can be past twice, happens when changing CPU threads.
results = work.results
s.results = work.results
s.makeWork(work.block)
s.notifyWork()
makeWork(work.block)
// Notify and requested URLs of the new work availability
notifyWork()
case work := <-ethash.fetchWorkCh:
case work := <-s.fetchWorkCh:
// Return current mining work to remote miner.
if currentBlock == nil {
if s.currentBlock == nil {
work.errc <- errNoMiningWork
} else {
work.res <- currentWork
work.res <- s.currentWork
}
case result := <-ethash.submitWorkCh:
case result := <-s.submitWorkCh:
// Verify submitted PoW solution based on maintained mining blocks.
if submitWork(result.nonce, result.mixDigest, result.hash) {
if s.submitWork(result.nonce, result.mixDigest, result.hash) {
result.errc <- nil
} else {
result.errc <- errInvalidSealResult
}
case result := <-ethash.submitRateCh:
case result := <-s.submitRateCh:
// Trace remote sealer's hash rate by submitted value.
rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
s.rates[result.id] = hashrate{rate: result.rate, ping: time.Now()}
close(result.done)
case req := <-ethash.fetchRateCh:
case req := <-s.fetchRateCh:
// Gather all hash rate submitted by remote sealer.
var total uint64
for _, rate := range rates {
for _, rate := range s.rates {
// this could overflow
total += rate.rate
}
@@ -347,25 +315,126 @@ func (ethash *Ethash) remote(notify []string, noverify bool) {
case <-ticker.C:
// Clear stale submitted hash rate.
for id, rate := range rates {
for id, rate := range s.rates {
if time.Since(rate.ping) > 10*time.Second {
delete(rates, id)
delete(s.rates, id)
}
}
// Clear stale pending blocks
if currentBlock != nil {
for hash, block := range works {
if block.NumberU64()+staleThreshold <= currentBlock.NumberU64() {
delete(works, hash)
if s.currentBlock != nil {
for hash, block := range s.works {
if block.NumberU64()+staleThreshold <= s.currentBlock.NumberU64() {
delete(s.works, hash)
}
}
}
case errc := <-ethash.exitCh:
// Exit remote loop if ethash is closed and return relevant error.
errc <- nil
log.Trace("Ethash remote sealer is exiting")
case <-s.requestExit:
return
}
}
}
// makeWork creates a work package for external miner.
//
// The work package consists of 3 strings:
// result[0], 32 bytes hex encoded current block header pow-hash
// result[1], 32 bytes hex encoded seed hash used for DAG
// result[2], 32 bytes hex encoded boundary condition ("target"), 2^256/difficulty
// result[3], hex encoded block number
func (s *remoteSealer) makeWork(block *types.Block) {
hash := s.ethash.SealHash(block.Header())
s.currentWork[0] = hash.Hex()
s.currentWork[1] = common.BytesToHash(SeedHash(block.NumberU64())).Hex()
s.currentWork[2] = common.BytesToHash(new(big.Int).Div(two256, block.Difficulty()).Bytes()).Hex()
s.currentWork[3] = hexutil.EncodeBig(block.Number())
// Trace the seal work fetched by remote sealer.
s.currentBlock = block
s.works[hash] = block
}
// notifyWork notifies all the specified mining endpoints of the availability of
// new work to be processed.
func (s *remoteSealer) notifyWork() {
work := s.currentWork
blob, _ := json.Marshal(work)
s.reqWG.Add(len(s.notifyURLs))
for _, url := range s.notifyURLs {
go s.sendNotification(s.notifyCtx, url, blob, work)
}
}
func (s *remoteSealer) sendNotification(ctx context.Context, url string, json []byte, work [4]string) {
defer s.reqWG.Done()
req, err := http.NewRequest("POST", url, bytes.NewReader(json))
if err != nil {
s.ethash.config.Log.Warn("Can't create remote miner notification", "err", err)
return
}
ctx, cancel := context.WithTimeout(ctx, remoteSealerTimeout)
defer cancel()
req = req.WithContext(ctx)
req.Header.Set("Content-Type", "application/json")
resp, err := http.DefaultClient.Do(req)
if err != nil {
s.ethash.config.Log.Warn("Failed to notify remote miner", "err", err)
} else {
s.ethash.config.Log.Trace("Notified remote miner", "miner", url, "hash", work[0], "target", work[2])
resp.Body.Close()
}
}
// submitWork verifies the submitted pow solution, returning
// whether the solution was accepted or not (not can be both a bad pow as well as
// any other error, like no pending work or stale mining result).
func (s *remoteSealer) submitWork(nonce types.BlockNonce, mixDigest common.Hash, sealhash common.Hash) bool {
if s.currentBlock == nil {
s.ethash.config.Log.Error("Pending work without block", "sealhash", sealhash)
return false
}
// Make sure the work submitted is present
block := s.works[sealhash]
if block == nil {
s.ethash.config.Log.Warn("Work submitted but none pending", "sealhash", sealhash, "curnumber", s.currentBlock.NumberU64())
return false
}
// Verify the correctness of submitted result.
header := block.Header()
header.Nonce = nonce
header.MixDigest = mixDigest
start := time.Now()
if !s.noverify {
if err := s.ethash.verifySeal(nil, header, true); err != nil {
s.ethash.config.Log.Warn("Invalid proof-of-work submitted", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)), "err", err)
return false
}
}
// Make sure the result channel is assigned.
if s.results == nil {
s.ethash.config.Log.Warn("Ethash result channel is empty, submitted mining result is rejected")
return false
}
s.ethash.config.Log.Trace("Verified correct proof-of-work", "sealhash", sealhash, "elapsed", common.PrettyDuration(time.Since(start)))
// Solutions seems to be valid, return to the miner and notify acceptance.
solution := block.WithSeal(header)
// The submitted solution is within the scope of acceptance.
if solution.NumberU64()+staleThreshold > s.currentBlock.NumberU64() {
select {
case s.results <- solution:
s.ethash.config.Log.Debug("Work submitted is acceptable", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
return true
default:
s.ethash.config.Log.Warn("Sealing result is not read by miner", "mode", "remote", "sealhash", sealhash)
return false
}
}
// The submitted block is too old to accept, drop it.
s.ethash.config.Log.Warn("Work submitted is too old", "number", solution.NumberU64(), "sealhash", sealhash, "hash", solution.Hash())
return false
}

View File

@@ -20,59 +20,39 @@ import (
"encoding/json"
"io/ioutil"
"math/big"
"net"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/testlog"
"github.com/ethereum/go-ethereum/log"
)
// Tests whether remote HTTP servers are correctly notified of new work.
func TestRemoteNotify(t *testing.T) {
// Start a simple webserver to capture notifications
// Start a simple web server to capture notifications.
sink := make(chan [3]string)
server := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
blob, err := ioutil.ReadAll(req.Body)
if err != nil {
t.Fatalf("failed to read miner notification: %v", err)
}
var work [3]string
if err := json.Unmarshal(blob, &work); err != nil {
t.Fatalf("failed to unmarshal miner notification: %v", err)
}
sink <- work
}),
}
// Open a custom listener to extract its local address
listener, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("failed to open notification server: %v", err)
}
defer listener.Close()
go server.Serve(listener)
// Wait for server to start listening
var tries int
for tries = 0; tries < 10; tries++ {
conn, _ := net.DialTimeout("tcp", listener.Addr().String(), 1*time.Second)
if conn != nil {
break
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
blob, err := ioutil.ReadAll(req.Body)
if err != nil {
t.Errorf("failed to read miner notification: %v", err)
}
}
if tries == 10 {
t.Fatal("tcp listener not ready for more than 10 seconds")
}
var work [3]string
if err := json.Unmarshal(blob, &work); err != nil {
t.Errorf("failed to unmarshal miner notification: %v", err)
}
sink <- work
}))
defer server.Close()
// Create the custom ethash engine
ethash := NewTester([]string{"http://" + listener.Addr().String()}, false)
// Create the custom ethash engine.
ethash := NewTester([]string{server.URL}, false)
defer ethash.Close()
// Stream a work task and ensure the notification bubbles out
// Stream a work task and ensure the notification bubbles out.
header := &types.Header{Number: big.NewInt(1), Difficulty: big.NewInt(100)}
block := types.NewBlockWithHeader(header)
@@ -97,46 +77,37 @@ func TestRemoteNotify(t *testing.T) {
// Tests that pushing work packages fast to the miner doesn't cause any data race
// issues in the notifications.
func TestRemoteMultiNotify(t *testing.T) {
// Start a simple webserver to capture notifications
// Start a simple web server to capture notifications.
sink := make(chan [3]string, 64)
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
blob, err := ioutil.ReadAll(req.Body)
if err != nil {
t.Errorf("failed to read miner notification: %v", err)
}
var work [3]string
if err := json.Unmarshal(blob, &work); err != nil {
t.Errorf("failed to unmarshal miner notification: %v", err)
}
sink <- work
}))
defer server.Close()
server := &http.Server{
Handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
blob, err := ioutil.ReadAll(req.Body)
if err != nil {
t.Fatalf("failed to read miner notification: %v", err)
}
var work [3]string
if err := json.Unmarshal(blob, &work); err != nil {
t.Fatalf("failed to unmarshal miner notification: %v", err)
}
sink <- work
}),
}
// Open a custom listener to extract its local address
listener, err := net.Listen("tcp", "localhost:0")
if err != nil {
t.Fatalf("failed to open notification server: %v", err)
}
defer listener.Close()
go server.Serve(listener)
// Create the custom ethash engine
ethash := NewTester([]string{"http://" + listener.Addr().String()}, false)
// Create the custom ethash engine.
ethash := NewTester([]string{server.URL}, false)
ethash.config.Log = testlog.Logger(t, log.LvlWarn)
defer ethash.Close()
// Stream a lot of work task and ensure all the notifications bubble out
// Stream a lot of work task and ensure all the notifications bubble out.
for i := 0; i < cap(sink); i++ {
header := &types.Header{Number: big.NewInt(int64(i)), Difficulty: big.NewInt(100)}
block := types.NewBlockWithHeader(header)
ethash.Seal(nil, block, nil, nil)
}
for i := 0; i < cap(sink); i++ {
select {
case <-sink:
case <-time.After(3 * time.Second):
case <-time.After(10 * time.Second):
t.Fatalf("notification %d timed out", i)
}
}
@@ -206,10 +177,10 @@ func TestStaleSubmission(t *testing.T) {
select {
case res := <-results:
if res.Header().Nonce != fakeNonce {
t.Errorf("case %d block nonce mismatch, want %s, get %s", id+1, fakeNonce, res.Header().Nonce)
t.Errorf("case %d block nonce mismatch, want %x, get %x", id+1, fakeNonce, res.Header().Nonce)
}
if res.Header().MixDigest != fakeDigest {
t.Errorf("case %d block digest mismatch, want %s, get %s", id+1, fakeDigest, res.Header().MixDigest)
t.Errorf("case %d block digest mismatch, want %x, get %x", id+1, fakeDigest, res.Header().MixDigest)
}
if res.Header().Difficulty.Uint64() != c.headers[c.submitIndex].Difficulty.Uint64() {
t.Errorf("case %d block difficulty mismatch, want %d, get %d", id+1, c.headers[c.submitIndex].Difficulty, res.Header().Difficulty)

View File

@@ -20,14 +20,16 @@ import (
"encoding/json"
"fmt"
"io"
"reflect"
"strings"
"time"
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/accounts/scwallet"
"github.com/ethereum/go-ethereum/accounts/usbwallet"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/internal/jsre"
"github.com/ethereum/go-ethereum/rpc"
"github.com/robertkrimen/otto"
)
// bridge is a collection of JavaScript utility methods to bride the .js runtime
@@ -47,10 +49,18 @@ func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer) *br
}
}
func getJeth(vm *goja.Runtime) *goja.Object {
jeth := vm.Get("jeth")
if jeth == nil {
panic(vm.ToValue("jeth object does not exist"))
}
return jeth.ToObject(vm)
}
// NewAccount is a wrapper around the personal.newAccount RPC method that uses a
// non-echoing password prompt to acquire the passphrase and executes the original
// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call.
func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) NewAccount(call jsre.Call) (goja.Value, error) {
var (
password string
confirm string
@@ -58,52 +68,57 @@ func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) {
)
switch {
// No password was specified, prompt the user for it
case len(call.ArgumentList) == 0:
if password, err = b.prompter.PromptPassword("Password: "); err != nil {
throwJSException(err.Error())
case len(call.Arguments) == 0:
if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil {
return nil, err
}
if confirm, err = b.prompter.PromptPassword("Repeat password: "); err != nil {
throwJSException(err.Error())
if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil {
return nil, err
}
if password != confirm {
throwJSException("passwords don't match!")
return nil, fmt.Errorf("passwords don't match!")
}
// A single string password was specified, use that
case len(call.ArgumentList) == 1 && call.Argument(0).IsString():
password, _ = call.Argument(0).ToString()
// Otherwise fail with some error
case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil:
password = call.Argument(0).ToString().String()
default:
throwJSException("expected 0 or 1 string argument")
return nil, fmt.Errorf("expected 0 or 1 string argument")
}
// Password acquired, execute the call and return
ret, err := call.Otto.Call("jeth.newAccount", nil, password)
if err != nil {
throwJSException(err.Error())
newAccount, callable := goja.AssertFunction(getJeth(call.VM).Get("newAccount"))
if !callable {
return nil, fmt.Errorf("jeth.newAccount is not callable")
}
return ret
ret, err := newAccount(goja.Null(), call.VM.ToValue(password))
if err != nil {
return nil, err
}
return ret, nil
}
// OpenWallet is a wrapper around personal.openWallet which can interpret and
// react to certain error messages, such as the Trezor PIN matrix request.
func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) OpenWallet(call jsre.Call) (goja.Value, error) {
// Make sure we have a wallet specified to open
if !call.Argument(0).IsString() {
throwJSException("first argument must be the wallet URL to open")
if call.Argument(0).ToObject(call.VM).ClassName() != "String" {
return nil, fmt.Errorf("first argument must be the wallet URL to open")
}
wallet := call.Argument(0)
var passwd otto.Value
if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() {
passwd, _ = otto.ToValue("")
var passwd goja.Value
if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) {
passwd = call.VM.ToValue("")
} else {
passwd = call.Argument(1)
}
// Open the wallet and return if successful in itself
val, err := call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
if !callable {
return nil, fmt.Errorf("jeth.openWallet is not callable")
}
val, err := openWallet(goja.Null(), wallet, passwd)
if err == nil {
return val
return val, nil
}
// Wallet open failed, report error unless it's a PIN or PUK entry
@@ -111,32 +126,31 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
case strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()):
val, err = b.readPinAndReopenWallet(call)
if err == nil {
return val
return val, nil
}
val, err = b.readPassphraseAndReopenWallet(call)
if err != nil {
throwJSException(err.Error())
return nil, err
}
case strings.HasSuffix(err.Error(), scwallet.ErrPairingPasswordNeeded.Error()):
// PUK input requested, fetch from the user and call open again
if input, err := b.prompter.PromptPassword("Please enter the pairing password: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)
input, err := b.prompter.PromptPassword("Please enter the pairing password: ")
if err != nil {
return nil, err
}
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
passwd = call.VM.ToValue(input)
if val, err = openWallet(goja.Null(), wallet, passwd); err != nil {
if !strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()) {
throwJSException(err.Error())
return nil, err
} else {
// PIN input requested, fetch from the user and call open again
if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)
input, err := b.prompter.PromptPassword("Please enter current PIN: ")
if err != nil {
return nil, err
}
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
throwJSException(err.Error())
if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil {
return nil, err
}
}
}
@@ -144,52 +158,52 @@ func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) {
case strings.HasSuffix(err.Error(), scwallet.ErrPINUnblockNeeded.Error()):
// PIN unblock requested, fetch PUK and new PIN from the user
var pukpin string
if input, err := b.prompter.PromptPassword("Please enter current PUK: "); err != nil {
throwJSException(err.Error())
} else {
pukpin = input
input, err := b.prompter.PromptPassword("Please enter current PUK: ")
if err != nil {
return nil, err
}
if input, err := b.prompter.PromptPassword("Please enter new PIN: "); err != nil {
throwJSException(err.Error())
} else {
pukpin += input
pukpin = input
input, err = b.prompter.PromptPassword("Please enter new PIN: ")
if err != nil {
return nil, err
}
passwd, _ = otto.ToValue(pukpin)
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
throwJSException(err.Error())
pukpin += input
if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(pukpin)); err != nil {
return nil, err
}
case strings.HasSuffix(err.Error(), scwallet.ErrPINNeeded.Error()):
// PIN input requested, fetch from the user and call open again
if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)
input, err := b.prompter.PromptPassword("Please enter current PIN: ")
if err != nil {
return nil, err
}
if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil {
throwJSException(err.Error())
if val, err = openWallet(goja.Null(), wallet, call.VM.ToValue(input)); err != nil {
return nil, err
}
default:
// Unknown error occurred, drop to the user
throwJSException(err.Error())
return nil, err
}
return val
return val, nil
}
func (b *bridge) readPassphraseAndReopenWallet(call otto.FunctionCall) (otto.Value, error) {
var passwd otto.Value
func (b *bridge) readPassphraseAndReopenWallet(call jsre.Call) (goja.Value, error) {
wallet := call.Argument(0)
if input, err := b.prompter.PromptPassword("Please enter your password: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)
input, err := b.prompter.PromptPassword("Please enter your passphrase: ")
if err != nil {
return nil, err
}
return call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
if !callable {
return nil, fmt.Errorf("jeth.openWallet is not callable")
}
return openWallet(goja.Null(), wallet, call.VM.ToValue(input))
}
func (b *bridge) readPinAndReopenWallet(call otto.FunctionCall) (otto.Value, error) {
var passwd otto.Value
func (b *bridge) readPinAndReopenWallet(call jsre.Call) (goja.Value, error) {
wallet := call.Argument(0)
// Trezor PIN matrix input requested, display the matrix to the user and fetch the data
fmt.Fprintf(b.printer, "Look at the device for number positions\n\n")
@@ -199,155 +213,154 @@ func (b *bridge) readPinAndReopenWallet(call otto.FunctionCall) (otto.Value, err
fmt.Fprintf(b.printer, "--+---+--\n")
fmt.Fprintf(b.printer, "1 | 2 | 3\n\n")
if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)
input, err := b.prompter.PromptPassword("Please enter current PIN: ")
if err != nil {
return nil, err
}
return call.Otto.Call("jeth.openWallet", nil, wallet, passwd)
openWallet, callable := goja.AssertFunction(getJeth(call.VM).Get("openWallet"))
if !callable {
return nil, fmt.Errorf("jeth.openWallet is not callable")
}
return openWallet(goja.Null(), wallet, call.VM.ToValue(input))
}
// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that
// uses a non-echoing password prompt to acquire the passphrase and executes the
// original RPC method (saved in jeth.unlockAccount) with it to actually execute
// the RPC call.
func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) {
// Make sure we have an account specified to unlock
if !call.Argument(0).IsString() {
throwJSException("first argument must be the account to unlock")
func (b *bridge) UnlockAccount(call jsre.Call) (goja.Value, error) {
// Make sure we have an account specified to unlock.
if call.Argument(0).ExportType().Kind() != reflect.String {
return nil, fmt.Errorf("first argument must be the account to unlock")
}
account := call.Argument(0)
// If password is not given or is the null value, prompt the user for it
var passwd otto.Value
if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() {
// If password is not given or is the null value, prompt the user for it.
var passwd goja.Value
if goja.IsUndefined(call.Argument(1)) || goja.IsNull(call.Argument(1)) {
fmt.Fprintf(b.printer, "Unlock account %s\n", account)
if input, err := b.prompter.PromptPassword("Password: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)
input, err := b.prompter.PromptPassword("Passphrase: ")
if err != nil {
return nil, err
}
passwd = call.VM.ToValue(input)
} else {
if !call.Argument(1).IsString() {
throwJSException("password must be a string")
if call.Argument(1).ExportType().Kind() != reflect.String {
return nil, fmt.Errorf("password must be a string")
}
passwd = call.Argument(1)
}
// Third argument is the duration how long the account must be unlocked.
duration := otto.NullValue()
if call.Argument(2).IsDefined() && !call.Argument(2).IsNull() {
if !call.Argument(2).IsNumber() {
throwJSException("unlock duration must be a number")
// Third argument is the duration how long the account should be unlocked.
duration := goja.Null()
if !goja.IsUndefined(call.Argument(2)) && !goja.IsNull(call.Argument(2)) {
if !isNumber(call.Argument(2)) {
return nil, fmt.Errorf("unlock duration must be a number")
}
duration = call.Argument(2)
}
// Send the request to the backend and return
val, err := call.Otto.Call("jeth.unlockAccount", nil, account, passwd, duration)
if err != nil {
throwJSException(err.Error())
// Send the request to the backend and return.
unlockAccount, callable := goja.AssertFunction(getJeth(call.VM).Get("unlockAccount"))
if !callable {
return nil, fmt.Errorf("jeth.unlockAccount is not callable")
}
return val
return unlockAccount(goja.Null(), account, passwd, duration)
}
// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password
// prompt to acquire the passphrase and executes the original RPC method (saved in
// jeth.sign) with it to actually execute the RPC call.
func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) Sign(call jsre.Call) (goja.Value, error) {
var (
message = call.Argument(0)
account = call.Argument(1)
passwd = call.Argument(2)
)
if !message.IsString() {
throwJSException("first argument must be the message to sign")
if message.ExportType().Kind() != reflect.String {
return nil, fmt.Errorf("first argument must be the message to sign")
}
if !account.IsString() {
throwJSException("second argument must be the account to sign with")
if account.ExportType().Kind() != reflect.String {
return nil, fmt.Errorf("second argument must be the account to sign with")
}
// if the password is not given or null ask the user and ensure password is a string
if passwd.IsUndefined() || passwd.IsNull() {
if goja.IsUndefined(passwd) || goja.IsNull(passwd) {
fmt.Fprintf(b.printer, "Give password for account %s\n", account)
if input, err := b.prompter.PromptPassword("Password: "); err != nil {
throwJSException(err.Error())
} else {
passwd, _ = otto.ToValue(input)
input, err := b.prompter.PromptPassword("Password: ")
if err != nil {
return nil, err
}
}
if !passwd.IsString() {
throwJSException("third argument must be the password to unlock the account")
passwd = call.VM.ToValue(input)
} else if passwd.ExportType().Kind() != reflect.String {
return nil, fmt.Errorf("third argument must be the password to unlock the account")
}
// Send the request to the backend and return
val, err := call.Otto.Call("jeth.sign", nil, message, account, passwd)
if err != nil {
throwJSException(err.Error())
sign, callable := goja.AssertFunction(getJeth(call.VM).Get("unlockAccount"))
if !callable {
return nil, fmt.Errorf("jeth.unlockAccount is not callable")
}
return val
return sign(goja.Null(), message, account, passwd)
}
// Sleep will block the console for the specified number of seconds.
func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) {
if call.Argument(0).IsNumber() {
sleep, _ := call.Argument(0).ToInteger()
time.Sleep(time.Duration(sleep) * time.Second)
return otto.TrueValue()
func (b *bridge) Sleep(call jsre.Call) (goja.Value, error) {
if !isNumber(call.Argument(0)) {
return nil, fmt.Errorf("usage: sleep(<number of seconds>)")
}
return throwJSException("usage: sleep(<number of seconds>)")
sleep := call.Argument(0).ToFloat()
time.Sleep(time.Duration(sleep * float64(time.Second)))
return call.VM.ToValue(true), nil
}
// SleepBlocks will block the console for a specified number of new blocks optionally
// until the given timeout is reached.
func (b *bridge) SleepBlocks(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) SleepBlocks(call jsre.Call) (goja.Value, error) {
// Parse the input parameters for the sleep.
var (
blocks = int64(0)
sleep = int64(9999999999999999) // indefinitely
)
// Parse the input parameters for the sleep
nArgs := len(call.ArgumentList)
nArgs := len(call.Arguments)
if nArgs == 0 {
throwJSException("usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
return nil, fmt.Errorf("usage: sleepBlocks(<n blocks>[, max sleep in seconds])")
}
if nArgs >= 1 {
if call.Argument(0).IsNumber() {
blocks, _ = call.Argument(0).ToInteger()
} else {
throwJSException("expected number as first argument")
if !isNumber(call.Argument(0)) {
return nil, fmt.Errorf("expected number as first argument")
}
blocks = call.Argument(0).ToInteger()
}
if nArgs >= 2 {
if call.Argument(1).IsNumber() {
sleep, _ = call.Argument(1).ToInteger()
} else {
throwJSException("expected number as second argument")
if isNumber(call.Argument(1)) {
return nil, fmt.Errorf("expected number as second argument")
}
sleep = call.Argument(1).ToInteger()
}
// go through the console, this will allow web3 to call the appropriate
// callbacks if a delayed response or notification is received.
blockNumber := func() int64 {
result, err := call.Otto.Run("eth.blockNumber")
if err != nil {
throwJSException(err.Error())
}
block, err := result.ToInteger()
if err != nil {
throwJSException(err.Error())
}
return block
}
// Poll the current block number until either it ot a timeout is reached
targetBlockNr := blockNumber() + blocks
deadline := time.Now().Add(time.Duration(sleep) * time.Second)
// Poll the current block number until either it or a timeout is reached.
var (
deadline = time.Now().Add(time.Duration(sleep) * time.Second)
lastNumber = ^hexutil.Uint64(0)
)
for time.Now().Before(deadline) {
if blockNumber() >= targetBlockNr {
return otto.TrueValue()
var number hexutil.Uint64
err := b.client.Call(&number, "eth_blockNumber")
if err != nil {
return nil, err
}
if number != lastNumber {
lastNumber = number
blocks--
}
if blocks <= 0 {
break
}
time.Sleep(time.Second)
}
return otto.FalseValue()
return call.VM.ToValue(true), nil
}
type jsonrpcCall struct {
@@ -357,15 +370,15 @@ type jsonrpcCall struct {
}
// Send implements the web3 provider "send" method.
func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
func (b *bridge) Send(call jsre.Call) (goja.Value, error) {
// Remarshal the request into a Go value.
JSON, _ := call.Otto.Object("JSON")
reqVal, err := JSON.Call("stringify", call.Argument(0))
reqVal, err := call.Argument(0).ToObject(call.VM).MarshalJSON()
if err != nil {
throwJSException(err.Error())
return nil, err
}
var (
rawReq = reqVal.String()
rawReq = string(reqVal)
dec = json.NewDecoder(strings.NewReader(rawReq))
reqs []jsonrpcCall
batch bool
@@ -381,10 +394,12 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
}
// Execute the requests.
resps, _ := call.Otto.Object("new Array()")
var resps []*goja.Object
for _, req := range reqs {
resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`)
resp := call.VM.NewObject()
resp.Set("jsonrpc", "2.0")
resp.Set("id", req.ID)
var result json.RawMessage
err = b.client.Call(&result, req.Method, req.Params...)
switch err := err.(type) {
@@ -392,9 +407,14 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
if result == nil {
// Special case null because it is decoded as an empty
// raw message for some reason.
resp.Set("result", otto.NullValue())
resp.Set("result", goja.Null())
} else {
resultVal, err := JSON.Call("parse", string(result))
JSON := call.VM.Get("JSON").ToObject(call.VM)
parse, callable := goja.AssertFunction(JSON.Get("parse"))
if !callable {
return nil, fmt.Errorf("JSON.parse is not a function")
}
resultVal, err := parse(goja.Null(), call.VM.ToValue(string(result)))
if err != nil {
setError(resp, -32603, err.Error())
} else {
@@ -406,33 +426,38 @@ func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) {
default:
setError(resp, -32603, err.Error())
}
resps.Call("push", resp)
resps = append(resps, resp)
}
// Return the responses either to the callback (if supplied)
// or directly as the return value.
var result goja.Value
if batch {
response = resps.Value()
result = call.VM.ToValue(resps)
} else {
response, _ = resps.Get("0")
result = resps[0]
}
if fn := call.Argument(1); fn.Class() == "Function" {
fn.Call(otto.NullValue(), otto.NullValue(), response)
return otto.UndefinedValue()
if fn, isFunc := goja.AssertFunction(call.Argument(1)); isFunc {
fn(goja.Null(), goja.Null(), result)
return goja.Undefined(), nil
}
return response
return result, nil
}
func setError(resp *otto.Object, code int, msg string) {
func setError(resp *goja.Object, code int, msg string) {
resp.Set("error", map[string]interface{}{"code": code, "message": msg})
}
// throwJSException panics on an otto.Value. The Otto VM will recover from the
// Go panic and throw msg as a JavaScript error.
func throwJSException(msg interface{}) otto.Value {
val, err := otto.ToValue(msg)
if err != nil {
log.Error("Failed to serialize JavaScript exception", "exception", msg, "err", err)
}
panic(val)
// isNumber returns true if input value is a JS number.
func isNumber(v goja.Value) bool {
k := v.ExportType().Kind()
return k >= reflect.Int && k <= reflect.Float64
}
func getObject(vm *goja.Runtime, name string) *goja.Object {
v := vm.Get(name)
if v == nil {
return nil
}
return v.ToObject(vm)
}

View File

@@ -28,12 +28,13 @@ import (
"strings"
"syscall"
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/internal/jsre"
"github.com/ethereum/go-ethereum/internal/jsre/deps"
"github.com/ethereum/go-ethereum/internal/web3ext"
"github.com/ethereum/go-ethereum/rpc"
"github.com/mattn/go-colorable"
"github.com/peterh/liner"
"github.com/robertkrimen/otto"
)
var (
@@ -86,6 +87,7 @@ func New(config Config) (*Console, error) {
if config.Printer == nil {
config.Printer = colorable.NewColorableStdout()
}
// Initialize the console and return
console := &Console{
client: config.Client,
@@ -107,110 +109,35 @@ func New(config Config) (*Console, error) {
// init retrieves the available APIs from the remote RPC provider and initializes
// the console's JavaScript namespaces based on the exposed modules.
func (c *Console) init(preload []string) error {
// Initialize the JavaScript <-> Go RPC bridge
c.initConsoleObject()
// Initialize the JavaScript <-> Go RPC bridge.
bridge := newBridge(c.client, c.prompter, c.printer)
c.jsre.Set("jeth", struct{}{})
jethObj, _ := c.jsre.Get("jeth")
jethObj.Object().Set("send", bridge.Send)
jethObj.Object().Set("sendAsync", bridge.Send)
consoleObj, _ := c.jsre.Get("console")
consoleObj.Object().Set("log", c.consoleOutput)
consoleObj.Object().Set("error", c.consoleOutput)
// Load all the internal utility JavaScript libraries
if err := c.jsre.Compile("bignumber.js", jsre.BignumberJs); err != nil {
return fmt.Errorf("bignumber.js: %v", err)
}
if err := c.jsre.Compile("web3.js", jsre.Web3Js); err != nil {
return fmt.Errorf("web3.js: %v", err)
}
if _, err := c.jsre.Run("var Web3 = require('web3');"); err != nil {
return fmt.Errorf("web3 require: %v", err)
}
if _, err := c.jsre.Run("var web3 = new Web3(jeth);"); err != nil {
return fmt.Errorf("web3 provider: %v", err)
}
// Load the supported APIs into the JavaScript runtime environment
apis, err := c.client.SupportedModules()
if err != nil {
return fmt.Errorf("api modules: %v", err)
}
flatten := "var eth = web3.eth; var personal = web3.personal; "
for api := range apis {
if api == "web3" {
continue // manually mapped or ignore
}
if file, ok := web3ext.Modules[api]; ok {
// Load our extension for the module.
if err = c.jsre.Compile(fmt.Sprintf("%s.js", api), file); err != nil {
return fmt.Errorf("%s.js: %v", api, err)
}
flatten += fmt.Sprintf("var %s = web3.%s; ", api, api)
} else if obj, err := c.jsre.Run("web3." + api); err == nil && obj.IsObject() {
// Enable web3.js built-in extension if available.
flatten += fmt.Sprintf("var %s = web3.%s; ", api, api)
}
}
if _, err = c.jsre.Run(flatten); err != nil {
return fmt.Errorf("namespace flattening: %v", err)
}
// Initialize the global name register (disabled for now)
//c.jsre.Run(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`)
// If the console is in interactive mode, instrument password related methods to query the user
if c.prompter != nil {
// Retrieve the account management object to instrument
personal, err := c.jsre.Get("personal")
if err != nil {
return err
}
// Override the openWallet, unlockAccount, newAccount and sign methods since
// these require user interaction. Assign these method in the Console the
// original web3 callbacks. These will be called by the jeth.* methods after
// they got the password from the user and send the original web3 request to
// the backend.
if obj := personal.Object(); obj != nil { // make sure the personal api is enabled over the interface
if _, err = c.jsre.Run(`jeth.openWallet = personal.openWallet;`); err != nil {
return fmt.Errorf("personal.openWallet: %v", err)
}
if _, err = c.jsre.Run(`jeth.unlockAccount = personal.unlockAccount;`); err != nil {
return fmt.Errorf("personal.unlockAccount: %v", err)
}
if _, err = c.jsre.Run(`jeth.newAccount = personal.newAccount;`); err != nil {
return fmt.Errorf("personal.newAccount: %v", err)
}
if _, err = c.jsre.Run(`jeth.sign = personal.sign;`); err != nil {
return fmt.Errorf("personal.sign: %v", err)
}
obj.Set("openWallet", bridge.OpenWallet)
obj.Set("unlockAccount", bridge.UnlockAccount)
obj.Set("newAccount", bridge.NewAccount)
obj.Set("sign", bridge.Sign)
}
}
// The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer.
admin, err := c.jsre.Get("admin")
if err != nil {
if err := c.initWeb3(bridge); err != nil {
return err
}
if obj := admin.Object(); obj != nil { // make sure the admin api is enabled over the interface
obj.Set("sleepBlocks", bridge.SleepBlocks)
obj.Set("sleep", bridge.Sleep)
obj.Set("clearHistory", c.clearHistory)
if err := c.initExtensions(); err != nil {
return err
}
// Preload any JavaScript files before starting the console
// Add bridge overrides for web3.js functionality.
c.jsre.Do(func(vm *goja.Runtime) {
c.initAdmin(vm, bridge)
c.initPersonal(vm, bridge)
})
// Preload JavaScript files.
for _, path := range preload {
if err := c.jsre.Exec(path); err != nil {
failure := err.Error()
if ottoErr, ok := err.(*otto.Error); ok {
failure = ottoErr.String()
if gojaErr, ok := err.(*goja.Exception); ok {
failure = gojaErr.String()
}
return fmt.Errorf("%s: %v", path, failure)
}
}
// Configure the console's input prompter for scrollback and tab completion
// Configure the input prompter for history and tab completion.
if c.prompter != nil {
if content, err := ioutil.ReadFile(c.histPath); err != nil {
c.prompter.SetHistory(nil)
@@ -223,6 +150,102 @@ func (c *Console) init(preload []string) error {
return nil
}
func (c *Console) initConsoleObject() {
c.jsre.Do(func(vm *goja.Runtime) {
console := vm.NewObject()
console.Set("log", c.consoleOutput)
console.Set("error", c.consoleOutput)
vm.Set("console", console)
})
}
func (c *Console) initWeb3(bridge *bridge) error {
bnJS := string(deps.MustAsset("bignumber.js"))
web3JS := string(deps.MustAsset("web3.js"))
if err := c.jsre.Compile("bignumber.js", bnJS); err != nil {
return fmt.Errorf("bignumber.js: %v", err)
}
if err := c.jsre.Compile("web3.js", web3JS); err != nil {
return fmt.Errorf("web3.js: %v", err)
}
if _, err := c.jsre.Run("var Web3 = require('web3');"); err != nil {
return fmt.Errorf("web3 require: %v", err)
}
var err error
c.jsre.Do(func(vm *goja.Runtime) {
transport := vm.NewObject()
transport.Set("send", jsre.MakeCallback(vm, bridge.Send))
transport.Set("sendAsync", jsre.MakeCallback(vm, bridge.Send))
vm.Set("_consoleWeb3Transport", transport)
_, err = vm.RunString("var web3 = new Web3(_consoleWeb3Transport)")
})
return err
}
// initExtensions loads and registers web3.js extensions.
func (c *Console) initExtensions() error {
// Compute aliases from server-provided modules.
apis, err := c.client.SupportedModules()
if err != nil {
return fmt.Errorf("api modules: %v", err)
}
aliases := map[string]struct{}{"eth": {}, "personal": {}}
for api := range apis {
if api == "web3" {
continue
}
aliases[api] = struct{}{}
if file, ok := web3ext.Modules[api]; ok {
if err = c.jsre.Compile(api+".js", file); err != nil {
return fmt.Errorf("%s.js: %v", api, err)
}
}
}
// Apply aliases.
c.jsre.Do(func(vm *goja.Runtime) {
web3 := getObject(vm, "web3")
for name := range aliases {
if v := web3.Get(name); v != nil {
vm.Set(name, v)
}
}
})
return nil
}
// initAdmin creates additional admin APIs implemented by the bridge.
func (c *Console) initAdmin(vm *goja.Runtime, bridge *bridge) {
if admin := getObject(vm, "admin"); admin != nil {
admin.Set("sleepBlocks", jsre.MakeCallback(vm, bridge.SleepBlocks))
admin.Set("sleep", jsre.MakeCallback(vm, bridge.Sleep))
admin.Set("clearHistory", c.clearHistory)
}
}
// initPersonal redirects account-related API methods through the bridge.
//
// If the console is in interactive mode and the 'personal' API is available, override
// the openWallet, unlockAccount, newAccount and sign methods since these require user
// interaction. The original web3 callbacks are stored in 'jeth'. These will be called
// by the bridge after the prompt and send the original web3 request to the backend.
func (c *Console) initPersonal(vm *goja.Runtime, bridge *bridge) {
personal := getObject(vm, "personal")
if personal == nil || c.prompter == nil {
return
}
jeth := vm.NewObject()
vm.Set("jeth", jeth)
jeth.Set("openWallet", personal.Get("openWallet"))
jeth.Set("unlockAccount", personal.Get("unlockAccount"))
jeth.Set("newAccount", personal.Get("newAccount"))
jeth.Set("sign", personal.Get("sign"))
personal.Set("openWallet", jsre.MakeCallback(vm, bridge.OpenWallet))
personal.Set("unlockAccount", jsre.MakeCallback(vm, bridge.UnlockAccount))
personal.Set("newAccount", jsre.MakeCallback(vm, bridge.NewAccount))
personal.Set("sign", jsre.MakeCallback(vm, bridge.Sign))
}
func (c *Console) clearHistory() {
c.history = nil
c.prompter.ClearHistory()
@@ -235,13 +258,13 @@ func (c *Console) clearHistory() {
// consoleOutput is an override for the console.log and console.error methods to
// stream the output into the configured output stream instead of stdout.
func (c *Console) consoleOutput(call otto.FunctionCall) otto.Value {
func (c *Console) consoleOutput(call goja.FunctionCall) goja.Value {
var output []string
for _, argument := range call.ArgumentList {
for _, argument := range call.Arguments {
output = append(output, fmt.Sprintf("%v", argument))
}
fmt.Fprintln(c.printer, strings.Join(output, " "))
return otto.Value{}
return goja.Null()
}
// AutoCompleteInput is a pre-assembled word completer to be used by the user
@@ -304,13 +327,13 @@ func (c *Console) Welcome() {
// Evaluate executes code and pretty prints the result to the specified output
// stream.
func (c *Console) Evaluate(statement string) error {
func (c *Console) Evaluate(statement string) {
defer func() {
if r := recover(); r != nil {
fmt.Fprintf(c.printer, "[native] error: %v\n", r)
}
}()
return c.jsre.Evaluate(statement, c.printer)
c.jsre.Evaluate(statement, c.printer)
}
// Interactive starts an interactive user session, where input is propted from

View File

@@ -289,7 +289,7 @@ func TestPrettyError(t *testing.T) {
defer tester.Close(t)
tester.console.Evaluate("throw 'hello'")
want := jsre.ErrorColor("hello") + "\n"
want := jsre.ErrorColor("hello") + "\n\tat <eval>:1:7(1)\n\n"
if output := tester.output.String(); output != want {
t.Fatalf("pretty error mismatch: have %s, want %s", output, want)
}

View File

@@ -29,8 +29,9 @@ import (
"github.com/ethereum/go-ethereum/core/types"
)
// CheckpointOracle is a Go wrapper around an on-chain light client checkpoint oracle.
// CheckpointOracle is a Go wrapper around an on-chain checkpoint oracle contract.
type CheckpointOracle struct {
address common.Address
contract *contract.CheckpointOracle
}
@@ -40,7 +41,12 @@ func NewCheckpointOracle(contractAddr common.Address, backend bind.ContractBacke
if err != nil {
return nil, err
}
return &CheckpointOracle{contract: c}, nil
return &CheckpointOracle{address: contractAddr, contract: c}, nil
}
// ContractAddr returns the address of contract.
func (oracle *CheckpointOracle) ContractAddr() common.Address {
return oracle.address
}
// Contract returns the underlying contract instance.

View File

@@ -60,6 +60,14 @@ func TestLexer(t *testing.T) {
input: "0123abc",
tokens: []token{{typ: lineStart}, {typ: number, text: "0123"}, {typ: element, text: "abc"}, {typ: eof}},
},
{
input: "@foo",
tokens: []token{{typ: lineStart}, {typ: label, text: "foo"}, {typ: eof}},
},
{
input: "@label123",
tokens: []token{{typ: lineStart}, {typ: label, text: "label123"}, {typ: eof}},
},
}
for _, test := range tests {

View File

@@ -234,7 +234,7 @@ func lexComment(l *lexer) stateFn {
// the lex text state function to advance the parsing
// process.
func lexLabel(l *lexer) stateFn {
l.acceptRun(Alpha + "_")
l.acceptRun(Alpha + "_" + Numbers)
l.emit(label)

View File

@@ -23,6 +23,7 @@ import (
"io"
"math/big"
mrand "math/rand"
"sort"
"sync"
"sync/atomic"
"time"
@@ -42,7 +43,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"github.com/hashicorp/golang-lru"
lru "github.com/hashicorp/golang-lru"
)
var (
@@ -406,6 +407,11 @@ func (bc *BlockChain) SetHead(head uint64) error {
}
}
rawdb.WriteHeadBlockHash(db, newHeadBlock.Hash())
// Degrade the chain markers if they are explicitly reverted.
// In theory we should update all in-memory markers in the
// last step, however the direction of SetHead is from high
// to low, so it's safe the update in-memory markers directly.
bc.currentBlock.Store(newHeadBlock)
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
}
@@ -418,6 +424,11 @@ func (bc *BlockChain) SetHead(head uint64) error {
newHeadFastBlock = bc.genesisBlock
}
rawdb.WriteHeadFastBlockHash(db, newHeadFastBlock.Hash())
// Degrade the chain markers if they are explicitly reverted.
// In theory we should update all in-memory markers in the
// last step, however the direction of SetHead is from high
// to low, so it's safe the update in-memory markers directly.
bc.currentFastBlock.Store(newHeadFastBlock)
headFastBlockGauge.Update(int64(newHeadFastBlock.NumberU64()))
}
@@ -537,21 +548,22 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
defer bc.chainmu.Unlock()
// Prepare the genesis block and reinitialise the chain
if err := bc.hc.WriteTd(genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()); err != nil {
log.Crit("Failed to write genesis block TD", "err", err)
batch := bc.db.NewBatch()
rawdb.WriteTd(batch, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty())
rawdb.WriteBlock(batch, genesis)
if err := batch.Write(); err != nil {
log.Crit("Failed to write genesis block", "err", err)
}
rawdb.WriteBlock(bc.db, genesis)
bc.writeHeadBlock(genesis)
// Last update all in-memory chain markers
bc.genesisBlock = genesis
bc.insert(bc.genesisBlock)
bc.currentBlock.Store(bc.genesisBlock)
headBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
bc.hc.SetGenesis(bc.genesisBlock.Header())
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
bc.currentFastBlock.Store(bc.genesisBlock)
headFastBlockGauge.Update(int64(bc.genesisBlock.NumberU64()))
return nil
}
@@ -609,31 +621,39 @@ func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
return nil
}
// insert injects a new head block into the current block chain. This method
// writeHeadBlock injects a new head block into the current block chain. This method
// assumes that the block is indeed a true head. It will also reset the head
// header and the head fast sync block to this very same block if they are older
// or if they are on a different side chain.
//
// Note, this function assumes that the `mu` mutex is held!
func (bc *BlockChain) insert(block *types.Block) {
func (bc *BlockChain) writeHeadBlock(block *types.Block) {
// If the block is on a side chain or an unknown one, force other heads onto it too
updateHeads := rawdb.ReadCanonicalHash(bc.db, block.NumberU64()) != block.Hash()
// Add the block to the canonical chain number scheme and mark as the head
rawdb.WriteCanonicalHash(bc.db, block.Hash(), block.NumberU64())
rawdb.WriteHeadBlockHash(bc.db, block.Hash())
bc.currentBlock.Store(block)
headBlockGauge.Update(int64(block.NumberU64()))
batch := bc.db.NewBatch()
rawdb.WriteCanonicalHash(batch, block.Hash(), block.NumberU64())
rawdb.WriteTxLookupEntries(batch, block)
rawdb.WriteHeadBlockHash(batch, block.Hash())
// If the block is better than our head or is on a different chain, force update heads
if updateHeads {
rawdb.WriteHeadHeaderHash(batch, block.Hash())
rawdb.WriteHeadFastBlockHash(batch, block.Hash())
}
// Flush the whole batch into the disk, exit the node if failed
if err := batch.Write(); err != nil {
log.Crit("Failed to update chain indexes and markers", "err", err)
}
// Update all in-memory chain markers in the last step
if updateHeads {
bc.hc.SetCurrentHeader(block.Header())
rawdb.WriteHeadFastBlockHash(bc.db, block.Hash())
bc.currentFastBlock.Store(block)
headFastBlockGauge.Update(int64(block.NumberU64()))
}
bc.currentBlock.Store(block)
headBlockGauge.Update(int64(block.NumberU64()))
}
// Genesis retrieves the chain's genesis block.
@@ -855,8 +875,9 @@ func (bc *BlockChain) procFutureBlocks() {
}
}
if len(blocks) > 0 {
types.BlockBy(types.Number).Sort(blocks)
sort.Slice(blocks, func(i, j int) bool {
return blocks[i].NumberU64() < blocks[j].NumberU64()
})
// Insert one by one as chain insertion needs contiguous ancestry between blocks
for i := range blocks {
bc.InsertChain(blocks[i : i+1])
@@ -879,26 +900,36 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
bc.chainmu.Lock()
defer bc.chainmu.Unlock()
batch := bc.db.NewBatch()
for i := len(chain) - 1; i >= 0; i-- {
hash := chain[i]
// Degrade the chain markers if they are explicitly reverted.
// In theory we should update all in-memory markers in the
// last step, however the direction of rollback is from high
// to low, so it's safe the update in-memory markers directly.
currentHeader := bc.hc.CurrentHeader()
if currentHeader.Hash() == hash {
bc.hc.SetCurrentHeader(bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1))
newHeadHeader := bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1)
rawdb.WriteHeadHeaderHash(batch, currentHeader.ParentHash)
bc.hc.SetCurrentHeader(newHeadHeader)
}
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash {
newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1)
rawdb.WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
rawdb.WriteHeadFastBlockHash(batch, currentFastBlock.ParentHash())
bc.currentFastBlock.Store(newFastBlock)
headFastBlockGauge.Update(int64(newFastBlock.NumberU64()))
}
if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash {
newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1)
rawdb.WriteHeadBlockHash(bc.db, newBlock.Hash())
rawdb.WriteHeadBlockHash(batch, currentBlock.ParentHash())
bc.currentBlock.Store(newBlock)
headBlockGauge.Update(int64(newBlock.NumberU64()))
}
}
if err := batch.Write(); err != nil {
log.Crit("Failed to rollback chain markers", "err", err)
}
// Truncate ancient data which exceeds the current header.
//
// Notably, it can happen that system crashes without truncating the ancient data
@@ -1061,7 +1092,6 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
}
// Don't collect too much in-memory, write it out every 100K blocks
if len(deleted) > 100000 {
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
if err := bc.db.Sync(); err != nil {
return 0, err
@@ -1170,7 +1200,9 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receiptChain[i])
rawdb.WriteTxLookupEntries(batch, block)
stats.processed++
// Write everything belongs to the blocks into the database. So that
// we can ensure all components of body is completed(body, receipts,
// tx indexes)
if batch.ValueSize() >= ethdb.IdealBatchSize {
if err := batch.Write(); err != nil {
return 0, err
@@ -1178,7 +1210,11 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
size += batch.ValueSize()
batch.Reset()
}
stats.processed++
}
// Write everything belongs to the blocks into the database. So that
// we can ensure all components of body is completed(body, receipts,
// tx indexes)
if batch.ValueSize() > 0 {
size += batch.ValueSize()
if err := batch.Write(); err != nil {
@@ -1229,11 +1265,12 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (e
bc.wg.Add(1)
defer bc.wg.Done()
if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), td); err != nil {
return err
batch := bc.db.NewBatch()
rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td)
rawdb.WriteBlock(batch, block)
if err := batch.Write(); err != nil {
log.Crit("Failed to write block into disk", "err", err)
}
rawdb.WriteBlock(bc.db, block)
return nil
}
@@ -1249,25 +1286,21 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
return err
}
}
// Write the positional metadata for transaction/receipt lookups.
// Preimages here is empty, ignore it.
rawdb.WriteTxLookupEntries(bc.db, block)
bc.insert(block)
bc.writeHeadBlock(block)
return nil
}
// WriteBlockWithState writes the block and all associated state to the database.
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
bc.chainmu.Lock()
defer bc.chainmu.Unlock()
return bc.writeBlockWithState(block, receipts, state)
return bc.writeBlockWithState(block, receipts, logs, state, emitHeadEvent)
}
// writeBlockWithState writes the block and all associated state to the database,
// but is expects the chain mutex to be held.
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, state *state.StateDB) (status WriteStatus, err error) {
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
bc.wg.Add(1)
defer bc.wg.Done()
@@ -1281,12 +1314,19 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
externTd := new(big.Int).Add(block.Difficulty(), ptd)
// Irrelevant of the canonical status, write the block itself to the database
if err := bc.hc.WriteTd(block.Hash(), block.NumberU64(), externTd); err != nil {
return NonStatTy, err
// Irrelevant of the canonical status, write the block itself to the database.
//
// Note all the components of block(td, hash->number map, header, body, receipts)
// should be written atomically. BlockBatch is used for containing all components.
blockBatch := bc.db.NewBatch()
rawdb.WriteTd(blockBatch, block.Hash(), block.NumberU64(), externTd)
rawdb.WriteBlock(blockBatch, block)
rawdb.WriteReceipts(blockBatch, block.Hash(), block.NumberU64(), receipts)
rawdb.WritePreimages(blockBatch, state.Preimages())
if err := blockBatch.Write(); err != nil {
log.Crit("Failed to write block into disk", "err", err)
}
rawdb.WriteBlock(bc.db, block)
// Commit all cached state changes into underlying memory database.
root, err := state.Commit(bc.chainConfig.IsEIP158(block.Number()))
if err != nil {
return NonStatTy, err
@@ -1345,11 +1385,6 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
}
}
}
// Write other block data using a batch.
batch := bc.db.NewBatch()
rawdb.WriteReceipts(batch, block.Hash(), block.NumberU64(), receipts)
// If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
@@ -1375,23 +1410,32 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
return NonStatTy, err
}
}
// Write the positional metadata for transaction/receipt lookups and preimages
rawdb.WriteTxLookupEntries(batch, block)
rawdb.WritePreimages(batch, state.Preimages())
status = CanonStatTy
} else {
status = SideStatTy
}
if err := batch.Write(); err != nil {
return NonStatTy, err
}
// Set new head.
if status == CanonStatTy {
bc.insert(block)
bc.writeHeadBlock(block)
}
bc.futureBlocks.Remove(block.Hash())
if status == CanonStatTy {
bc.chainFeed.Send(ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
if len(logs) > 0 {
bc.logsFeed.Send(logs)
}
// In theory we should fire a ChainHeadEvent when we inject
// a canonical block, but sometimes we can insert a batch of
// canonicial blocks. Avoid firing too much ChainHeadEvents,
// we will fire an accumulated ChainHeadEvent and disable fire
// event here.
if emitHeadEvent {
bc.chainHeadFeed.Send(ChainHeadEvent{Block: block})
}
} else {
bc.chainSideFeed.Send(ChainSideEvent{Block: block})
}
return status, nil
}
@@ -1442,11 +1486,10 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// Pre-checks passed, start the full block imports
bc.wg.Add(1)
bc.chainmu.Lock()
n, events, logs, err := bc.insertChain(chain, true)
n, err := bc.insertChain(chain, true)
bc.chainmu.Unlock()
bc.wg.Done()
bc.PostChainEvents(events, logs)
return n, err
}
@@ -1458,23 +1501,24 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// racey behaviour. If a sidechain import is in progress, and the historic state
// is imported, but then new canon-head is added before the actual sidechain
// completes, then the historic state could be pruned again
func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []interface{}, []*types.Log, error) {
func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, error) {
// If the chain is terminating, don't even bother starting up
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
return 0, nil, nil, nil
return 0, nil
}
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
// A queued approach to delivering events. This is generally
// faster than direct delivery and requires much less mutex
// acquiring.
var (
stats = insertStats{startTime: mclock.Now()}
events = make([]interface{}, 0, len(chain))
lastCanon *types.Block
coalescedLogs []*types.Log
stats = insertStats{startTime: mclock.Now()}
lastCanon *types.Block
)
// Fire a single chain head event if we've progressed the chain
defer func() {
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
bc.chainHeadFeed.Send(ChainHeadEvent{lastCanon})
}
}()
// Start the parallel header verifier
headers := make([]*types.Header, len(chain))
seals := make([]bool, len(chain))
@@ -1524,7 +1568,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
for block != nil && err == ErrKnownBlock {
log.Debug("Writing previously known block", "number", block.Number(), "hash", block.Hash())
if err := bc.writeKnownBlock(block); err != nil {
return it.index, nil, nil, err
return it.index, err
}
lastCanon = block
@@ -1543,7 +1587,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
for block != nil && (it.index == 0 || err == consensus.ErrUnknownAncestor) {
log.Debug("Future block, postponing import", "number", block.Number(), "hash", block.Hash())
if err := bc.addFutureBlock(block); err != nil {
return it.index, events, coalescedLogs, err
return it.index, err
}
block, err = it.next()
}
@@ -1551,14 +1595,14 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
stats.ignored += it.remaining()
// If there are any still remaining, mark as ignored
return it.index, events, coalescedLogs, err
return it.index, err
// Some other error occurred, abort
case err != nil:
bc.futureBlocks.Remove(block.Hash())
stats.ignored += len(it.chain)
bc.reportBlock(block, nil, err)
return it.index, events, coalescedLogs, err
return it.index, err
}
// No validation errors for the first block (or chain prefix skipped)
for ; block != nil && err == nil || err == ErrKnownBlock; block, err = it.next() {
@@ -1570,7 +1614,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
// If the header is a banned one, straight out abort
if BadHashes[block.Hash()] {
bc.reportBlock(block, nil, ErrBlacklistedHash)
return it.index, events, coalescedLogs, ErrBlacklistedHash
return it.index, ErrBlacklistedHash
}
// If the block is known (in the middle of the chain), it's a special case for
// Clique blocks where they can share state among each other, so importing an
@@ -1587,15 +1631,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
"root", block.Root())
if err := bc.writeKnownBlock(block); err != nil {
return it.index, nil, nil, err
return it.index, err
}
stats.processed++
// We can assume that logs are empty here, since the only way for consecutive
// Clique blocks to have the same state is if there are no transactions.
events = append(events, ChainEvent{block, block.Hash(), nil})
lastCanon = block
continue
}
// Retrieve the parent block and it's state to execute on top
@@ -1607,23 +1649,22 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
}
statedb, err := state.New(parent.Root, bc.stateCache)
if err != nil {
return it.index, events, coalescedLogs, err
return it.index, err
}
// If we have a followup block, run that against the current state to pre-cache
// transactions and probabilistically some of the account/storage trie nodes.
var followupInterrupt uint32
if !bc.cacheConfig.TrieCleanNoPrefetch {
if followup, err := it.peek(); followup != nil && err == nil {
go func(start time.Time) {
throwaway, _ := state.New(parent.Root, bc.stateCache)
bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, &followupInterrupt)
throwaway, _ := state.New(parent.Root, bc.stateCache)
go func(start time.Time, followup *types.Block, throwaway *state.StateDB, interrupt *uint32) {
bc.prefetcher.Prefetch(followup, throwaway, bc.vmConfig, interrupt)
blockPrefetchExecuteTimer.Update(time.Since(start))
if atomic.LoadUint32(&followupInterrupt) == 1 {
if atomic.LoadUint32(interrupt) == 1 {
blockPrefetchInterruptMeter.Mark(1)
}
}(time.Now())
}(time.Now(), followup, throwaway, &followupInterrupt)
}
}
// Process block using the parent state as reference point
@@ -1632,7 +1673,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
if err != nil {
bc.reportBlock(block, receipts, err)
atomic.StoreUint32(&followupInterrupt, 1)
return it.index, events, coalescedLogs, err
return it.index, err
}
// Update the metrics touched during block processing
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them
@@ -1651,7 +1692,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
if err := bc.validator.ValidateState(block, statedb, receipts, usedGas); err != nil {
bc.reportBlock(block, receipts, err)
atomic.StoreUint32(&followupInterrupt, 1)
return it.index, events, coalescedLogs, err
return it.index, err
}
proctime := time.Since(start)
@@ -1663,10 +1704,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
// Write the block to the chain and get the status.
substart = time.Now()
status, err := bc.writeBlockWithState(block, receipts, statedb)
status, err := bc.writeBlockWithState(block, receipts, logs, statedb, false)
if err != nil {
atomic.StoreUint32(&followupInterrupt, 1)
return it.index, events, coalescedLogs, err
return it.index, err
}
atomic.StoreUint32(&followupInterrupt, 1)
@@ -1684,8 +1725,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
"elapsed", common.PrettyDuration(time.Since(start)),
"root", block.Root())
coalescedLogs = append(coalescedLogs, logs...)
events = append(events, ChainEvent{block, block.Hash(), logs})
lastCanon = block
// Only count canonical blocks for GC processing time
@@ -1696,7 +1735,6 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
"txs", len(block.Transactions()), "gas", block.GasUsed(), "uncles", len(block.Uncles()),
"root", block.Root())
events = append(events, ChainSideEvent{block})
default:
// This in theory is impossible, but lets be nice to our future selves and leave
@@ -1715,24 +1753,20 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
// Any blocks remaining here? The only ones we care about are the future ones
if block != nil && err == consensus.ErrFutureBlock {
if err := bc.addFutureBlock(block); err != nil {
return it.index, events, coalescedLogs, err
return it.index, err
}
block, err = it.next()
for ; block != nil && err == consensus.ErrUnknownAncestor; block, err = it.next() {
if err := bc.addFutureBlock(block); err != nil {
return it.index, events, coalescedLogs, err
return it.index, err
}
stats.queued++
}
}
stats.ignored += it.remaining()
// Append a single chain head event if we've progressed the chain
if lastCanon != nil && bc.CurrentBlock().Hash() == lastCanon.Hash() {
events = append(events, ChainHeadEvent{lastCanon})
}
return it.index, events, coalescedLogs, err
return it.index, err
}
// insertSideChain is called when an import batch hits upon a pruned ancestor
@@ -1741,7 +1775,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, []
//
// The method writes all (header-and-body-valid) blocks to disk, then tries to
// switch over to the new chain if the TD exceeded the current chain.
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (int, []interface{}, []*types.Log, error) {
func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (int, error) {
var (
externTd *big.Int
current = bc.CurrentBlock()
@@ -1777,7 +1811,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
// If someone legitimately side-mines blocks, they would still be imported as usual. However,
// we cannot risk writing unverified blocks to disk when they obviously target the pruning
// mechanism.
return it.index, nil, nil, errors.New("sidechain ghost-state attack")
return it.index, errors.New("sidechain ghost-state attack")
}
}
if externTd == nil {
@@ -1788,7 +1822,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
if !bc.HasBlock(block.Hash(), block.NumberU64()) {
start := time.Now()
if err := bc.writeBlockWithoutState(block, externTd); err != nil {
return it.index, nil, nil, err
return it.index, err
}
log.Debug("Injected sidechain block", "number", block.Number(), "hash", block.Hash(),
"diff", block.Difficulty(), "elapsed", common.PrettyDuration(time.Since(start)),
@@ -1805,7 +1839,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
localTd := bc.GetTd(current.Hash(), current.NumberU64())
if localTd.Cmp(externTd) > 0 {
log.Info("Sidechain written to disk", "start", it.first().NumberU64(), "end", it.previous().Number, "sidetd", externTd, "localtd", localTd)
return it.index, nil, nil, err
return it.index, err
}
// Gather all the sidechain hashes (full blocks may be memory heavy)
var (
@@ -1820,7 +1854,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
parent = bc.GetHeader(parent.ParentHash, parent.Number.Uint64()-1)
}
if parent == nil {
return it.index, nil, nil, errors.New("missing parent")
return it.index, errors.New("missing parent")
}
// Import all the pruned blocks to make the state available
var (
@@ -1839,15 +1873,15 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
// memory here.
if len(blocks) >= 2048 || memory > 64*1024*1024 {
log.Info("Importing heavy sidechain segment", "blocks", len(blocks), "start", blocks[0].NumberU64(), "end", block.NumberU64())
if _, _, _, err := bc.insertChain(blocks, false); err != nil {
return 0, nil, nil, err
if _, err := bc.insertChain(blocks, false); err != nil {
return 0, err
}
blocks, memory = blocks[:0], 0
// If the chain is terminating, stop processing blocks
if atomic.LoadInt32(&bc.procInterrupt) == 1 {
log.Debug("Premature abort during blocks processing")
return 0, nil, nil, nil
return 0, nil
}
}
}
@@ -1855,7 +1889,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
log.Info("Importing sidechain segment", "start", blocks[0].NumberU64(), "end", blocks[len(blocks)-1].NumberU64())
return bc.insertChain(blocks, false)
}
return 0, nil, nil, nil
return 0, nil
}
// reorg takes two blocks, an old chain and a new chain and will reconstruct the
@@ -1870,11 +1904,11 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
deletedTxs types.Transactions
addedTxs types.Transactions
deletedLogs []*types.Log
rebirthLogs []*types.Log
deletedLogs [][]*types.Log
rebirthLogs [][]*types.Log
// collectLogs collects the logs that were generated during the
// processing of the block that corresponds with the given hash.
// collectLogs collects the logs that were generated or removed during
// the processing of the block that corresponds with the given hash.
// These logs are later announced as deleted or reborn
collectLogs = func(hash common.Hash, removed bool) {
number := bc.hc.GetBlockNumber(hash)
@@ -1882,17 +1916,39 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
return
}
receipts := rawdb.ReadReceipts(bc.db, hash, *number, bc.chainConfig)
var logs []*types.Log
for _, receipt := range receipts {
for _, log := range receipt.Logs {
l := *log
if removed {
l.Removed = true
deletedLogs = append(deletedLogs, &l)
} else {
rebirthLogs = append(rebirthLogs, &l)
}
logs = append(logs, &l)
}
}
if len(logs) > 0 {
if removed {
deletedLogs = append(deletedLogs, logs)
} else {
rebirthLogs = append(rebirthLogs, logs)
}
}
}
// mergeLogs returns a merged log slice with specified sort order.
mergeLogs = func(logs [][]*types.Log, reverse bool) []*types.Log {
var ret []*types.Log
if reverse {
for i := len(logs) - 1; i >= 0; i-- {
ret = append(ret, logs[i]...)
}
} else {
for i := 0; i < len(logs); i++ {
ret = append(ret, logs[i]...)
}
}
return ret
}
)
// Reduce the longer chain to the same number as the shorter one
@@ -1959,20 +2015,19 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// taking care of the proper incremental order.
for i := len(newChain) - 1; i >= 1; i-- {
// Insert the block in the canonical way, re-writing history
bc.insert(newChain[i])
bc.writeHeadBlock(newChain[i])
// Collect reborn logs due to chain reorg
collectLogs(newChain[i].Hash(), false)
// Write lookup entries for hash based transaction/receipt searches
rawdb.WriteTxLookupEntries(bc.db, newChain[i])
// Collect the new added transactions.
addedTxs = append(addedTxs, newChain[i].Transactions()...)
}
// When transactions get deleted from the database, the receipts that were
// created in the fork must also be deleted
batch := bc.db.NewBatch()
// Delete useless indexes right now which includes the non-canonical
// transaction indexes, canonical chain indexes which above the head.
indexesBatch := bc.db.NewBatch()
for _, tx := range types.TxDifference(deletedTxs, addedTxs) {
rawdb.DeleteTxLookupEntry(batch, tx.Hash())
rawdb.DeleteTxLookupEntry(indexesBatch, tx.Hash())
}
// Delete any canonical number assignments above the new head
number := bc.CurrentBlock().NumberU64()
@@ -1981,54 +2036,29 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
if hash == (common.Hash{}) {
break
}
rawdb.DeleteCanonicalHash(batch, i)
rawdb.DeleteCanonicalHash(indexesBatch, i)
}
if err := indexesBatch.Write(); err != nil {
log.Crit("Failed to delete useless indexes", "err", err)
}
batch.Write()
// If any logs need to be fired, do it now. In theory we could avoid creating
// this goroutine if there are no events to fire, but realistcally that only
// ever happens if we're reorging empty blocks, which will only happen on idle
// networks where performance is not an issue either way.
//
// TODO(karalabe): Can we get rid of the goroutine somehow to guarantee correct
// event ordering?
go func() {
if len(deletedLogs) > 0 {
bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
if len(deletedLogs) > 0 {
bc.rmLogsFeed.Send(RemovedLogsEvent{mergeLogs(deletedLogs, true)})
}
if len(rebirthLogs) > 0 {
bc.logsFeed.Send(mergeLogs(rebirthLogs, false))
}
if len(oldChain) > 0 {
for i := len(oldChain) - 1; i >= 0; i-- {
bc.chainSideFeed.Send(ChainSideEvent{Block: oldChain[i]})
}
if len(rebirthLogs) > 0 {
bc.logsFeed.Send(rebirthLogs)
}
if len(oldChain) > 0 {
for _, block := range oldChain {
bc.chainSideFeed.Send(ChainSideEvent{Block: block})
}
}
}()
}
return nil
}
// PostChainEvents iterates over the events generated by a chain insertion and
// posts them into the event feed.
// TODO: Should not expose PostChainEvents. The chain events should be posted in WriteBlock.
func (bc *BlockChain) PostChainEvents(events []interface{}, logs []*types.Log) {
// post event logs for further processing
if logs != nil {
bc.logsFeed.Send(logs)
}
for _, event := range events {
switch ev := event.(type) {
case ChainEvent:
bc.chainFeed.Send(ev)
case ChainHeadEvent:
bc.chainHeadFeed.Send(ev)
case ChainSideEvent:
bc.chainSideFeed.Send(ev)
}
}
}
func (bc *BlockChain) update() {
futureTimer := time.NewTicker(5 * time.Second)
defer futureTimer.Stop()

View File

@@ -22,6 +22,7 @@ import (
"math/big"
"math/rand"
"os"
"reflect"
"sync"
"testing"
"time"
@@ -960,16 +961,20 @@ func TestLogReorgs(t *testing.T) {
}
chain, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert forked chain: %v", err)
}
timeout := time.NewTimer(1 * time.Second)
select {
case ev := <-rmLogsCh:
done := make(chan struct{})
go func() {
ev := <-rmLogsCh
if len(ev.Logs) == 0 {
t.Error("expected logs")
}
close(done)
}()
if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert forked chain: %v", err)
}
timeout := time.NewTimer(1 * time.Second)
select {
case <-done:
case <-timeout.C:
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
}
@@ -982,39 +987,47 @@ func TestLogRebirth(t *testing.T) {
db = rawdb.NewMemoryDatabase()
// this code generates a log
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
genesis = gspec.MustCommit(db)
signer = types.NewEIP155Signer(gspec.Config.ChainID)
newLogCh = make(chan bool)
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
genesis = gspec.MustCommit(db)
signer = types.NewEIP155Signer(gspec.Config.ChainID)
newLogCh = make(chan bool)
removeLogCh = make(chan bool)
)
// listenNewLog checks whether the received logs number is equal with expected.
listenNewLog := func(sink chan []*types.Log, expect int) {
// validateLogEvent checks whether the received logs number is equal with expected.
validateLogEvent := func(sink interface{}, result chan bool, expect int) {
chanval := reflect.ValueOf(sink)
chantyp := chanval.Type()
if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.RecvDir == 0 {
t.Fatalf("invalid channel, given type %v", chantyp)
}
cnt := 0
var recv []reflect.Value
timeout := time.After(1 * time.Second)
cases := []reflect.SelectCase{{Chan: chanval, Dir: reflect.SelectRecv}, {Chan: reflect.ValueOf(timeout), Dir: reflect.SelectRecv}}
for {
select {
case logs := <-sink:
cnt += len(logs)
case <-time.NewTimer(5 * time.Second).C:
// new logs timeout
newLogCh <- false
chose, v, _ := reflect.Select(cases)
if chose == 1 {
// Not enough event received
result <- false
return
}
cnt += 1
recv = append(recv, v)
if cnt == expect {
break
} else if cnt > expect {
// redundant logs received
newLogCh <- false
return
}
}
select {
case <-sink:
// redundant logs received
newLogCh <- false
case <-time.NewTimer(100 * time.Millisecond).C:
newLogCh <- true
done := time.After(50 * time.Millisecond)
cases = cases[:1]
cases = append(cases, reflect.SelectCase{Chan: reflect.ValueOf(done), Dir: reflect.SelectRecv})
chose, _, _ := reflect.Select(cases)
// If chose equal 0, it means receiving redundant events.
if chose == 1 {
result <- true
} else {
result <- false
}
}
@@ -1038,12 +1051,12 @@ func TestLogRebirth(t *testing.T) {
})
// Spawn a goroutine to receive log events
go listenNewLog(logsCh, 1)
go validateLogEvent(logsCh, newLogCh, 1)
if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert chain: %v", err)
}
if !<-newLogCh {
t.Fatalf("failed to receive new log event")
t.Fatal("failed to receive new log event")
}
// Generate long reorg chain
@@ -1060,40 +1073,31 @@ func TestLogRebirth(t *testing.T) {
})
// Spawn a goroutine to receive log events
go listenNewLog(logsCh, 1)
go validateLogEvent(logsCh, newLogCh, 1)
go validateLogEvent(rmLogsCh, removeLogCh, 1)
if _, err := blockchain.InsertChain(forkChain); err != nil {
t.Fatalf("failed to insert forked chain: %v", err)
}
if !<-newLogCh {
t.Fatalf("failed to receive new log event")
t.Fatal("failed to receive new log event")
}
// Ensure removedLog events received
select {
case ev := <-rmLogsCh:
if len(ev.Logs) == 0 {
t.Error("expected logs")
}
case <-time.NewTimer(1 * time.Second).C:
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
if !<-removeLogCh {
t.Fatal("failed to receive removed log event")
}
newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
go listenNewLog(logsCh, 1)
go validateLogEvent(logsCh, newLogCh, 1)
go validateLogEvent(rmLogsCh, removeLogCh, 1)
if _, err := blockchain.InsertChain(newBlocks); err != nil {
t.Fatalf("failed to insert forked chain: %v", err)
}
// Ensure removedLog events received
select {
case ev := <-rmLogsCh:
if len(ev.Logs) == 0 {
t.Error("expected logs")
}
case <-time.NewTimer(1 * time.Second).C:
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
}
// Rebirth logs should omit a newLogEvent
if !<-newLogCh {
t.Fatalf("failed to receive new log event")
t.Fatal("failed to receive new log event")
}
// Ensure removedLog events received
if !<-removeLogCh {
t.Fatal("failed to receive removed log event")
}
}
@@ -1145,7 +1149,6 @@ func TestSideLogRebirth(t *testing.T) {
logsCh := make(chan []*types.Log)
blockchain.SubscribeLogsEvent(logsCh)
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
if i == 1 {
// Higher block difficulty
@@ -1289,14 +1292,17 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
continue // busy wait for canonical hash to be written
}
if ch != block.Hash() {
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
t.Errorf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
return
}
fb := rawdb.ReadBlock(blockchain.db, ch, block.NumberU64())
if fb == nil {
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
t.Errorf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
return
}
if fb.Hash() != block.Hash() {
t.Fatalf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex())
t.Errorf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex())
return
}
return
}
@@ -1916,13 +1922,6 @@ func testInsertKnownChainData(t *testing.T, typ string) {
inserter func(blocks []*types.Block, receipts []types.Receipts) error
asserter func(t *testing.T, block *types.Block)
)
headers, headers2 := make([]*types.Header, 0, len(blocks)), make([]*types.Header, 0, len(blocks2))
for _, block := range blocks {
headers = append(headers, block.Header())
}
for _, block := range blocks2 {
headers2 = append(headers2, block.Header())
}
if typ == "headers" {
inserter = func(blocks []*types.Block, receipts []types.Receipts) error {
headers := make([]*types.Header, 0, len(blocks))

View File

@@ -203,7 +203,7 @@ func (b *testChainIndexBackend) assertBlocks(headNum, failNum uint64) (uint64, b
}
func (b *testChainIndexBackend) reorg(headNum uint64) uint64 {
firstChanged := headNum / b.indexer.sectionSize
firstChanged := (headNum + 1) / b.indexer.sectionSize
if firstChanged < b.stored {
b.stored = firstChanged
}

View File

@@ -283,8 +283,7 @@ func makeBlockChain(parent *types.Block, n int, engine consensus.Engine, db ethd
}
type fakeChainReader struct {
config *params.ChainConfig
genesis *types.Block
config *params.ChainConfig
}
// Config returns the chain configuration.

View File

@@ -24,11 +24,6 @@ import (
// NewTxsEvent is posted when a batch of transactions enter the transaction pool.
type NewTxsEvent struct{ Txs []*types.Transaction }
// PendingLogsEvent is posted pre mining and notifies of pending logs.
type PendingLogsEvent struct {
Logs []*types.Log
}
// NewMinedBlockEvent is posted when a block has been imported.
type NewMinedBlockEvent struct{ Block *types.Block }

View File

@@ -60,24 +60,32 @@ func NewEVMContext(msg Message, header *types.Header, chain ChainContext, author
// GetHashFn returns a GetHashFunc which retrieves header hashes by number
func GetHashFn(ref *types.Header, chain ChainContext) func(n uint64) common.Hash {
var cache map[uint64]common.Hash
// Cache will initially contain [refHash.parent],
// Then fill up with [refHash.p, refHash.pp, refHash.ppp, ...]
var cache []common.Hash
return func(n uint64) common.Hash {
// If there's no hash cache yet, make one
if cache == nil {
cache = map[uint64]common.Hash{
ref.Number.Uint64() - 1: ref.ParentHash,
if len(cache) == 0 {
cache = append(cache, ref.ParentHash)
}
if idx := ref.Number.Uint64() - n - 1; idx < uint64(len(cache)) {
return cache[idx]
}
// No luck in the cache, but we can start iterating from the last element we already know
lastKnownHash := cache[len(cache)-1]
lastKnownNumber := ref.Number.Uint64() - uint64(len(cache))
for {
header := chain.GetHeader(lastKnownHash, lastKnownNumber)
if header == nil {
break
}
}
// Try to fulfill the request from the cache
if hash, ok := cache[n]; ok {
return hash
}
// Not cached, iterate the blocks and cache the hashes
for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
cache[header.Number.Uint64()-1] = header.ParentHash
if n == header.Number.Uint64()-1 {
return header.ParentHash
cache = append(cache, header.ParentHash)
lastKnownHash = header.ParentHash
lastKnownNumber = header.Number.Uint64() - 1
if n == lastKnownNumber {
return lastKnownHash
}
}
return common.Hash{}

View File

@@ -186,13 +186,6 @@ func newFilter(config *params.ChainConfig, genesis common.Hash, headfn func() ui
}
}
// checksum calculates the IEEE CRC32 checksum of a block number.
func checksum(fork uint64) uint32 {
var blob [8]byte
binary.BigEndian.PutUint64(blob[:], fork)
return crc32.ChecksumIEEE(blob[:])
}
// checksumUpdate calculates the next IEEE CRC32 checksum based on the previous
// one and a fork block number (equivalent to CRC32(original-blob || fork)).
func checksumUpdate(hash uint32, fork uint64) uint32 {

View File

@@ -57,8 +57,10 @@ func TestCreation(t *testing.T) {
{7279999, ID{Hash: checksumToBytes(0xa00bc324), Next: 7280000}}, // Last Byzantium block
{7280000, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // First and last Constantinople, first Petersburg block
{9068999, ID{Hash: checksumToBytes(0x668db0af), Next: 9069000}}, // Last Petersburg block
{9069000, ID{Hash: checksumToBytes(0x879d6e30), Next: 0}}, // Today Istanbul block
{10000000, ID{Hash: checksumToBytes(0x879d6e30), Next: 0}}, // Future Istanbul block
{9069000, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // First Istanbul and first Muir Glacier block
{9199999, ID{Hash: checksumToBytes(0x879d6e30), Next: 9200000}}, // Last Istanbul and first Muir Glacier block
{9200000, ID{Hash: checksumToBytes(0xe029e991), Next: 0}}, // First Muir Glacier block
{10000000, ID{Hash: checksumToBytes(0xe029e991), Next: 0}}, // Future Muir Glacier block
},
},
// Ropsten test cases
@@ -76,8 +78,10 @@ func TestCreation(t *testing.T) {
{4939393, ID{Hash: checksumToBytes(0x97b544f3), Next: 4939394}}, // Last Constantinople block
{4939394, ID{Hash: checksumToBytes(0xd6e2149b), Next: 6485846}}, // First Petersburg block
{6485845, ID{Hash: checksumToBytes(0xd6e2149b), Next: 6485846}}, // Last Petersburg block
{6485846, ID{Hash: checksumToBytes(0x4bc66396), Next: 0}}, // First Istanbul block
{7500000, ID{Hash: checksumToBytes(0x4bc66396), Next: 0}}, // Future Istanbul block
{6485846, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // First Istanbul block
{7117116, ID{Hash: checksumToBytes(0x4bc66396), Next: 7117117}}, // Last Istanbul block
{7117117, ID{Hash: checksumToBytes(0x6727ef90), Next: 0}}, // First Muir Glacier block
{7500000, ID{Hash: checksumToBytes(0x6727ef90), Next: 0}}, // Future
},
},
// Rinkeby test cases
@@ -181,11 +185,11 @@ func TestValidation(t *testing.T) {
// Local is mainnet Petersburg, remote is Rinkeby Petersburg.
{7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale},
// Local is mainnet Istanbul, far in the future. Remote announces Gopherium (non existing fork)
// Local is mainnet Muir Glacier, far in the future. Remote announces Gopherium (non existing fork)
// at some future block 88888888, for itself, but past block for local. Local is incompatible.
//
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
{88888888, ID{Hash: checksumToBytes(0x879d6e30), Next: 88888888}, ErrLocalIncompatibleOrStale},
{88888888, ID{Hash: checksumToBytes(0xe029e991), Next: 88888888}, ErrLocalIncompatibleOrStale},
// Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing
// fork) at block 7279999, before Petersburg. Local is incompatible.

View File

@@ -152,10 +152,10 @@ func (e *GenesisMismatchError) Error() string {
//
// The returned chain configuration is never nil.
func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
return SetupGenesisBlockWithOverride(db, genesis, nil)
return SetupGenesisBlockWithOverride(db, genesis, nil, nil)
}
func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideIstanbul *big.Int) (*params.ChainConfig, common.Hash, error) {
func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideIstanbul, overrideMuirGlacier *big.Int) (*params.ChainConfig, common.Hash, error) {
if genesis != nil && genesis.Config == nil {
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
}
@@ -207,6 +207,9 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override
if overrideIstanbul != nil {
newcfg.IstanbulBlock = overrideIstanbul
}
if overrideMuirGlacier != nil {
newcfg.MuirGlacierBlock = overrideMuirGlacier
}
if err := newcfg.CheckConfigForkOrder(); err != nil {
return newcfg, common.Hash{}, err
}
@@ -380,8 +383,7 @@ func DefaultGoerliGenesisBlock() *Genesis {
}
}
// DeveloperGenesisBlock returns the 'geth --dev' genesis block. Note, this must
// be seeded with the
// DeveloperGenesisBlock returns the 'geth --dev' genesis block.
func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
// Override the default period to the user requested one
config := *params.AllCliqueProtocolChanges

View File

@@ -45,6 +45,14 @@ const (
// HeaderChain implements the basic block header chain logic that is shared by
// core.BlockChain and light.LightChain. It is not usable in itself, only as
// a part of either structure.
//
// HeaderChain is responsible for maintaining the header chain including the
// header query and updating.
//
// The components maintained by headerchain includes: (1) total difficult
// (2) header (3) block hash -> number mapping (4) canonical number -> hash mapping
// and (5) head header flag.
//
// It is not thread safe either, the encapsulating chain structures should do
// the necessary mutex locking/unlocking.
type HeaderChain struct {
@@ -66,10 +74,8 @@ type HeaderChain struct {
engine consensus.Engine
}
// NewHeaderChain creates a new HeaderChain structure.
// getValidator should return the parent's validator
// procInterrupt points to the parent's interrupt semaphore
// wg points to the parent's shutdown wait group
// NewHeaderChain creates a new HeaderChain structure. ProcInterrupt points
// to the parent's interrupt semaphore.
func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine consensus.Engine, procInterrupt func() bool) (*HeaderChain, error) {
headerCache, _ := lru.New(headerCacheLimit)
tdCache, _ := lru.New(tdCacheLimit)
@@ -147,25 +153,33 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
externTd := new(big.Int).Add(header.Difficulty, ptd)
// Irrelevant of the canonical status, write the td and header to the database
if err := hc.WriteTd(hash, number, externTd); err != nil {
log.Crit("Failed to write header total difficulty", "err", err)
//
// Note all the components of header(td, hash->number index and header) should
// be written atomically.
headerBatch := hc.chainDb.NewBatch()
rawdb.WriteTd(headerBatch, hash, number, externTd)
rawdb.WriteHeader(headerBatch, header)
if err := headerBatch.Write(); err != nil {
log.Crit("Failed to write header into disk", "err", err)
}
rawdb.WriteHeader(hc.chainDb, header)
// If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
if externTd.Cmp(localTd) > 0 || (externTd.Cmp(localTd) == 0 && mrand.Float64() < 0.5) {
// If the header can be added into canonical chain, adjust the
// header chain markers(canonical indexes and head header flag).
//
// Note all markers should be written atomically.
// Delete any canonical number assignments above the new head
batch := hc.chainDb.NewBatch()
markerBatch := hc.chainDb.NewBatch()
for i := number + 1; ; i++ {
hash := rawdb.ReadCanonicalHash(hc.chainDb, i)
if hash == (common.Hash{}) {
break
}
rawdb.DeleteCanonicalHash(batch, i)
rawdb.DeleteCanonicalHash(markerBatch, i)
}
batch.Write()
// Overwrite any stale canonical number assignments
var (
@@ -174,16 +188,19 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
headHeader = hc.GetHeader(headHash, headNumber)
)
for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash {
rawdb.WriteCanonicalHash(hc.chainDb, headHash, headNumber)
rawdb.WriteCanonicalHash(markerBatch, headHash, headNumber)
headHash = headHeader.ParentHash
headNumber = headHeader.Number.Uint64() - 1
headHeader = hc.GetHeader(headHash, headNumber)
}
// Extend the canonical chain with the new header
rawdb.WriteCanonicalHash(hc.chainDb, hash, number)
rawdb.WriteHeadHeaderHash(hc.chainDb, hash)
rawdb.WriteCanonicalHash(markerBatch, hash, number)
rawdb.WriteHeadHeaderHash(markerBatch, hash)
if err := markerBatch.Write(); err != nil {
log.Crit("Failed to write header markers into disk", "err", err)
}
// Last step update all in-memory head header markers
hc.currentHeaderHash = hash
hc.currentHeader.Store(types.CopyHeader(header))
headHeaderGauge.Update(header.Number.Int64())
@@ -192,9 +209,9 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
} else {
status = SideStatTy
}
hc.tdCache.Add(hash, externTd)
hc.headerCache.Add(hash, header)
hc.numberCache.Add(hash, number)
return
}
@@ -396,14 +413,6 @@ func (hc *HeaderChain) GetTdByHash(hash common.Hash) *big.Int {
return hc.GetTd(hash, *number)
}
// WriteTd stores a block's total difficulty into the database, also caching it
// along the way.
func (hc *HeaderChain) WriteTd(hash common.Hash, number uint64, td *big.Int) error {
rawdb.WriteTd(hc.chainDb, hash, number, td)
hc.tdCache.Add(hash, new(big.Int).Set(td))
return nil
}
// GetHeader retrieves a block header from the database by hash and number,
// caching it if found.
func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header {
@@ -431,6 +440,8 @@ func (hc *HeaderChain) GetHeaderByHash(hash common.Hash) *types.Header {
}
// HasHeader checks if a block header is present in the database or not.
// In theory, if header is present in the database, all relative components
// like td and hash->number should be present too.
func (hc *HeaderChain) HasHeader(hash common.Hash, number uint64) bool {
if hc.numberCache.Contains(hash) || hc.headerCache.Contains(hash) {
return true
@@ -458,10 +469,9 @@ func (hc *HeaderChain) CurrentHeader() *types.Header {
return hc.currentHeader.Load().(*types.Header)
}
// SetCurrentHeader sets the current head header of the canonical chain.
// SetCurrentHeader sets the in-memory head header marker of the canonical chan
// as the given header.
func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
rawdb.WriteHeadHeaderHash(hc.chainDb, head.Hash())
hc.currentHeader.Store(head)
hc.currentHeaderHash = head.Hash()
headHeaderGauge.Update(head.Number.Int64())
@@ -500,11 +510,18 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
// first then remove the relative data from the database.
//
// Update head first(head fast block, head full block) before deleting the data.
markerBatch := hc.chainDb.NewBatch()
if updateFn != nil {
updateFn(hc.chainDb, parent)
updateFn(markerBatch, parent)
}
// Update head header then.
rawdb.WriteHeadHeaderHash(hc.chainDb, parentHash)
rawdb.WriteHeadHeaderHash(markerBatch, parentHash)
if err := markerBatch.Write(); err != nil {
log.Crit("Failed to update chain markers", "error", err)
}
hc.currentHeader.Store(parent)
hc.currentHeaderHash = parentHash
headHeaderGauge.Update(parent.Number.Int64())
// Remove the relative data from the database.
if delFn != nil {
@@ -514,13 +531,11 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
rawdb.DeleteHeader(batch, hash, num)
rawdb.DeleteTd(batch, hash, num)
rawdb.DeleteCanonicalHash(batch, num)
hc.currentHeader.Store(parent)
hc.currentHeaderHash = parentHash
headHeaderGauge.Update(parent.Number.Int64())
}
batch.Write()
// Flush all accumulated deletions.
if err := batch.Write(); err != nil {
log.Crit("Failed to rewind block", "error", err)
}
// Clear out any stale content from the caches
hc.headerCache.Purge()
hc.tdCache.Purge()

View File

@@ -1,87 +0,0 @@
// Copyright 2014 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"container/list"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
)
// Implement our EthTest Manager
type TestManager struct {
// stateManager *StateManager
eventMux *event.TypeMux
db ethdb.Database
txPool *TxPool
blockChain *BlockChain
Blocks []*types.Block
}
func (tm *TestManager) IsListening() bool {
return false
}
func (tm *TestManager) IsMining() bool {
return false
}
func (tm *TestManager) PeerCount() int {
return 0
}
func (tm *TestManager) Peers() *list.List {
return list.New()
}
func (tm *TestManager) BlockChain() *BlockChain {
return tm.blockChain
}
func (tm *TestManager) TxPool() *TxPool {
return tm.txPool
}
// func (tm *TestManager) StateManager() *StateManager {
// return tm.stateManager
// }
func (tm *TestManager) EventMux() *event.TypeMux {
return tm.eventMux
}
// func (tm *TestManager) KeyManager() *crypto.KeyManager {
// return nil
// }
func (tm *TestManager) Db() ethdb.Database {
return tm.db
}
func NewTestManager() *TestManager {
testManager := &TestManager{}
testManager.eventMux = new(event.TypeMux)
testManager.db = rawdb.NewMemoryDatabase()
// testManager.txPool = NewTxPool(testManager)
// testManager.blockChain = NewBlockChain(testManager)
// testManager.stateManager = NewStateManager(testManager)
return testManager
}

View File

@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
@@ -173,18 +174,27 @@ func WriteFastTrieProgress(db ethdb.KeyValueWriter, count uint64) {
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
// First try to look up the data in ancient database. Extra hash
// comparison is necessary since ancient database only maintains
// the canonical data.
data, _ := db.Ancient(freezerHeaderTable, number)
if len(data) == 0 {
data, _ = db.Get(headerKey(number, hash))
// In the background freezer is moving data from leveldb to flatten files.
// So during the first check for ancient db, the data is not yet in there,
// but when we reach into leveldb, the data was already moved. That would
// result in a not found error.
if len(data) == 0 {
data, _ = db.Ancient(freezerHeaderTable, number)
}
if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
return data
}
return data
// Then try to look up the data in leveldb.
data, _ = db.Get(headerKey(number, hash))
if len(data) > 0 {
return data
}
// In the background freezer is moving data from leveldb to flatten files.
// So during the first check for ancient db, the data is not yet in there,
// but when we reach into leveldb, the data was already moved. That would
// result in a not found error.
data, _ = db.Ancient(freezerHeaderTable, number)
if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
return data
}
return nil // Can't find the data anywhere.
}
// HasHeader verifies the existence of a block header corresponding to the hash.
@@ -251,18 +261,33 @@ func deleteHeaderWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
// First try to look up the data in ancient database. Extra hash
// comparison is necessary since ancient database only maintains
// the canonical data.
data, _ := db.Ancient(freezerBodiesTable, number)
if len(data) == 0 {
data, _ = db.Get(blockBodyKey(number, hash))
// In the background freezer is moving data from leveldb to flatten files.
// So during the first check for ancient db, the data is not yet in there,
// but when we reach into leveldb, the data was already moved. That would
// result in a not found error.
if len(data) == 0 {
data, _ = db.Ancient(freezerBodiesTable, number)
if len(data) > 0 {
h, _ := db.Ancient(freezerHashTable, number)
if common.BytesToHash(h) == hash {
return data
}
}
return data
// Then try to look up the data in leveldb.
data, _ = db.Get(blockBodyKey(number, hash))
if len(data) > 0 {
return data
}
// In the background freezer is moving data from leveldb to flatten files.
// So during the first check for ancient db, the data is not yet in there,
// but when we reach into leveldb, the data was already moved. That would
// result in a not found error.
data, _ = db.Ancient(freezerBodiesTable, number)
if len(data) > 0 {
h, _ := db.Ancient(freezerHashTable, number)
if common.BytesToHash(h) == hash {
return data
}
}
return nil // Can't find the data anywhere.
}
// WriteBodyRLP stores an RLP encoded block body into the database.
@@ -315,18 +340,33 @@ func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding.
func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
// First try to look up the data in ancient database. Extra hash
// comparison is necessary since ancient database only maintains
// the canonical data.
data, _ := db.Ancient(freezerDifficultyTable, number)
if len(data) == 0 {
data, _ = db.Get(headerTDKey(number, hash))
// In the background freezer is moving data from leveldb to flatten files.
// So during the first check for ancient db, the data is not yet in there,
// but when we reach into leveldb, the data was already moved. That would
// result in a not found error.
if len(data) == 0 {
data, _ = db.Ancient(freezerDifficultyTable, number)
if len(data) > 0 {
h, _ := db.Ancient(freezerHashTable, number)
if common.BytesToHash(h) == hash {
return data
}
}
return data
// Then try to look up the data in leveldb.
data, _ = db.Get(headerTDKey(number, hash))
if len(data) > 0 {
return data
}
// In the background freezer is moving data from leveldb to flatten files.
// So during the first check for ancient db, the data is not yet in there,
// but when we reach into leveldb, the data was already moved. That would
// result in a not found error.
data, _ = db.Ancient(freezerDifficultyTable, number)
if len(data) > 0 {
h, _ := db.Ancient(freezerHashTable, number)
if common.BytesToHash(h) == hash {
return data
}
}
return nil // Can't find the data anywhere.
}
// ReadTd retrieves a block's total difficulty corresponding to the hash.
@@ -375,18 +415,33 @@ func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
// First try to look up the data in ancient database. Extra hash
// comparison is necessary since ancient database only maintains
// the canonical data.
data, _ := db.Ancient(freezerReceiptTable, number)
if len(data) == 0 {
data, _ = db.Get(blockReceiptsKey(number, hash))
// In the background freezer is moving data from leveldb to flatten files.
// So during the first check for ancient db, the data is not yet in there,
// but when we reach into leveldb, the data was already moved. That would
// result in a not found error.
if len(data) == 0 {
data, _ = db.Ancient(freezerReceiptTable, number)
if len(data) > 0 {
h, _ := db.Ancient(freezerHashTable, number)
if common.BytesToHash(h) == hash {
return data
}
}
return data
// Then try to look up the data in leveldb.
data, _ = db.Get(blockReceiptsKey(number, hash))
if len(data) > 0 {
return data
}
// In the background freezer is moving data from leveldb to flatten files.
// So during the first check for ancient db, the data is not yet in there,
// but when we reach into leveldb, the data was already moved. That would
// result in a not found error.
data, _ = db.Ancient(freezerReceiptTable, number)
if len(data) > 0 {
h, _ := db.Ancient(freezerHashTable, number)
if common.BytesToHash(h) == hash {
return data
}
}
return nil // Can't find the data anywhere.
}
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.

View File

@@ -20,7 +20,9 @@ import (
"bytes"
"encoding/hex"
"fmt"
"io/ioutil"
"math/big"
"os"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -358,3 +360,67 @@ func checkReceiptsRLP(have, want types.Receipts) error {
}
return nil
}
func TestAncientStorage(t *testing.T) {
// Freezer style fast import the chain.
frdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("failed to create temp freezer dir: %v", err)
}
defer os.Remove(frdir)
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "")
if err != nil {
t.Fatalf("failed to create database with ancient backend")
}
// Create a test block
block := types.NewBlockWithHeader(&types.Header{
Number: big.NewInt(0),
Extra: []byte("test block"),
UncleHash: types.EmptyUncleHash,
TxHash: types.EmptyRootHash,
ReceiptHash: types.EmptyRootHash,
})
// Ensure nothing non-existent will be read
hash, number := block.Hash(), block.NumberU64()
if blob := ReadHeaderRLP(db, hash, number); len(blob) > 0 {
t.Fatalf("non existent header returned")
}
if blob := ReadBodyRLP(db, hash, number); len(blob) > 0 {
t.Fatalf("non existent body returned")
}
if blob := ReadReceiptsRLP(db, hash, number); len(blob) > 0 {
t.Fatalf("non existent receipts returned")
}
if blob := ReadTdRLP(db, hash, number); len(blob) > 0 {
t.Fatalf("non existent td returned")
}
// Write and verify the header in the database
WriteAncientBlock(db, block, nil, big.NewInt(100))
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
t.Fatalf("no header returned")
}
if blob := ReadBodyRLP(db, hash, number); len(blob) == 0 {
t.Fatalf("no body returned")
}
if blob := ReadReceiptsRLP(db, hash, number); len(blob) == 0 {
t.Fatalf("no receipts returned")
}
if blob := ReadTdRLP(db, hash, number); len(blob) == 0 {
t.Fatalf("no td returned")
}
// Use a fake hash for data retrieval, nothing should be returned.
fakeHash := common.BytesToHash([]byte{0x01, 0x02, 0x03})
if blob := ReadHeaderRLP(db, fakeHash, number); len(blob) != 0 {
t.Fatalf("invalid header returned")
}
if blob := ReadBodyRLP(db, fakeHash, number); len(blob) != 0 {
t.Fatalf("invalid body returned")
}
if blob := ReadReceiptsRLP(db, fakeHash, number); len(blob) != 0 {
t.Fatalf("invalid receipts returned")
}
if blob := ReadTdRLP(db, fakeHash, number); len(blob) != 0 {
t.Fatalf("invalid td returned")
}
}

View File

@@ -150,11 +150,10 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace st
}
// Database contains only older data than the freezer, this happens if the
// state was wiped and reinited from an existing freezer.
} else {
// Key-value store continues where the freezer left off, all is fine. We might
// have duplicate blocks (crash after freezer write but before kay-value store
// deletion, but that's fine).
}
// Otherwise, key-value store continues where the freezer left off, all is fine.
// We might have duplicate blocks (crash after freezer write but before key-value
// store deletion, but that's fine).
} else {
// If the freezer is empty, ensure nothing was moved yet from the key-value
// store, otherwise we'll end up missing data. We check block #1 to decide
@@ -167,9 +166,9 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace st
return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
}
// Block #1 is still in the database, we're allowed to init a new feezer
} else {
// The head header is still the genesis, we're allowed to init a new feezer
}
// Otherwise, the head header is still the genesis, we're allowed to init a new
// feezer.
}
}
// Freezer is consistent with the key-value database, permit combining the two

View File

@@ -55,10 +55,10 @@ func InitDatabaseFromFreezer(db ethdb.Database) error {
if n >= frozen {
return
}
// Retrieve the block from the freezer (no need for the hash, we pull by
// number from the freezer). If successful, pre-cache the block hash and
// the individual transaction hashes for storing into the database.
block := ReadBlock(db, common.Hash{}, n)
// Retrieve the block from the freezer. If successful, pre-cache
// the block hash and the individual transaction hashes for storing
// into the database.
block := ReadBlock(db, ReadCanonicalHash(db, n), n)
if block != nil {
block.Hash()
for _, tx := range block.Transactions() {

View File

@@ -41,14 +41,6 @@ func getChunk(size int, b int) []byte {
return data
}
func print(t *testing.T, f *freezerTable, item uint64) {
a, err := f.Retrieve(item)
if err != nil {
t.Fatal(err)
}
t.Logf("db[%d] = %x\n", item, a)
}
// TestFreezerBasics test initializing a freezertable from scratch, writing to the table,
// and reading it back.
func TestFreezerBasics(t *testing.T) {

View File

@@ -47,7 +47,9 @@ type Dump struct {
}
// iterativeDump is a 'collector'-implementation which dump output line-by-line iteratively
type iterativeDump json.Encoder
type iterativeDump struct {
*json.Encoder
}
// Collector interface which the state trie calls during iteration
type collector interface {
@@ -55,15 +57,15 @@ type collector interface {
onAccount(common.Address, DumpAccount)
}
func (self *Dump) onRoot(root common.Hash) {
self.Root = fmt.Sprintf("%x", root)
func (d *Dump) onRoot(root common.Hash) {
d.Root = fmt.Sprintf("%x", root)
}
func (self *Dump) onAccount(addr common.Address, account DumpAccount) {
self.Accounts[addr] = account
func (d *Dump) onAccount(addr common.Address, account DumpAccount) {
d.Accounts[addr] = account
}
func (self iterativeDump) onAccount(addr common.Address, account DumpAccount) {
func (d iterativeDump) onAccount(addr common.Address, account DumpAccount) {
dumpAccount := &DumpAccount{
Balance: account.Balance,
Nonce: account.Nonce,
@@ -77,25 +79,26 @@ func (self iterativeDump) onAccount(addr common.Address, account DumpAccount) {
if addr != (common.Address{}) {
dumpAccount.Address = &addr
}
(*json.Encoder)(&self).Encode(dumpAccount)
d.Encode(dumpAccount)
}
func (self iterativeDump) onRoot(root common.Hash) {
(*json.Encoder)(&self).Encode(struct {
func (d iterativeDump) onRoot(root common.Hash) {
d.Encode(struct {
Root common.Hash `json:"root"`
}{root})
}
func (self *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool) {
func (s *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissingPreimages bool) {
emptyAddress := (common.Address{})
missingPreimages := 0
c.onRoot(self.trie.Hash())
it := trie.NewIterator(self.trie.NodeIterator(nil))
c.onRoot(s.trie.Hash())
it := trie.NewIterator(s.trie.NodeIterator(nil))
for it.Next() {
var data Account
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
panic(err)
}
addr := common.BytesToAddress(self.trie.GetKey(it.Key))
addr := common.BytesToAddress(s.trie.GetKey(it.Key))
obj := newObject(nil, addr, data)
account := DumpAccount{
Balance: data.Balance.String(),
@@ -112,18 +115,18 @@ func (self *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissi
account.SecureKey = it.Key
}
if !excludeCode {
account.Code = common.Bytes2Hex(obj.Code(self.db))
account.Code = common.Bytes2Hex(obj.Code(s.db))
}
if !excludeStorage {
account.Storage = make(map[common.Hash]string)
storageIt := trie.NewIterator(obj.getTrie(self.db).NodeIterator(nil))
storageIt := trie.NewIterator(obj.getTrie(s.db).NodeIterator(nil))
for storageIt.Next() {
_, content, _, err := rlp.Split(storageIt.Value)
if err != nil {
log.Error("Failed to decode the value returned by iterator", "error", err)
continue
}
account.Storage[common.BytesToHash(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
account.Storage[common.BytesToHash(s.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(content)
}
}
c.onAccount(addr, account)
@@ -134,17 +137,17 @@ func (self *StateDB) dump(c collector, excludeCode, excludeStorage, excludeMissi
}
// RawDump returns the entire state an a single large object
func (self *StateDB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages bool) Dump {
func (s *StateDB) RawDump(excludeCode, excludeStorage, excludeMissingPreimages bool) Dump {
dump := &Dump{
Accounts: make(map[common.Address]DumpAccount),
}
self.dump(dump, excludeCode, excludeStorage, excludeMissingPreimages)
s.dump(dump, excludeCode, excludeStorage, excludeMissingPreimages)
return *dump
}
// Dump returns a JSON string representing the entire state as a single json-object
func (self *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool) []byte {
dump := self.RawDump(excludeCode, excludeStorage, excludeMissingPreimages)
func (s *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages bool) []byte {
dump := s.RawDump(excludeCode, excludeStorage, excludeMissingPreimages)
json, err := json.MarshalIndent(dump, "", " ")
if err != nil {
fmt.Println("dump err", err)
@@ -153,6 +156,6 @@ func (self *StateDB) Dump(excludeCode, excludeStorage, excludeMissingPreimages b
}
// IterativeDump dumps out accounts as json-objects, delimited by linebreaks on stdout
func (self *StateDB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) {
self.dump(iterativeDump(*output), excludeCode, excludeStorage, excludeMissingPreimages)
func (s *StateDB) IterativeDump(excludeCode, excludeStorage, excludeMissingPreimages bool, output *json.Encoder) {
s.dump(iterativeDump{output}, excludeCode, excludeStorage, excludeMissingPreimages)
}

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