Compare commits

..

134 Commits

Author SHA1 Message Date
Péter Szilágyi
0287d54847 params: release Geth v1.9.21 2020-09-09 11:22:11 +03:00
Péter Szilágyi
24562d9b0c Merge pull request #21534 from karalabe/cht-1.9.21
params: update CHTs for v1.9.21 release
2020-09-09 10:34:53 +03:00
Péter Szilágyi
dc681fc1f6 params: update CHTs for v1.9.21 release 2020-09-09 10:33:20 +03:00
Guillaume Ballet
86bcbb0d79 .github: remove whisper from CODEOWNERS (#21527) 2020-09-08 23:02:14 +03:00
Guillaume Ballet
066c75531d build: remove wnode from the list of packages binaries (#21526) 2020-09-08 16:13:48 +02:00
Martin Holst Swende
8327d1fdfc accounts/usbwallet, signer/core: show accounts from ledger legacy derivation paths (#21517)
* accounts/usbwallet, signer/core: un-hide accounts from ledger legacy derivation paths

* Update accounts/usbwallet/wallet.go

* Update signer/core/api.go

* Update signer/core/api.go
2020-09-08 14:07:55 +03:00
Guillaume Ballet
d54f2f2e5e whisper: remove whisper (#21487)
* whisper: remove whisper

* Update cmd/geth/config.go

Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>

* cmd/geth: warn on enabling whisper + remove more whisper deps

* mobile: remove all whisper references

Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
Co-authored-by: Martin Holst Swende <martin@swende.se>
2020-09-08 11:47:48 +03:00
Osoro Bironga
c5d28f0b27 accounts: abi/bid/backends; cleaned doc errors, camelCase refactors and anonymous variable assignments (#21514)
Co-authored-by: Osoro Bironga <osoro@doctaroo.com>
2020-09-07 13:07:15 +02:00
Marius van der Wijden
de971cc845 eth: added trace_call to trace on top of arbitrary blocks (#21338)
* eth: Added TraceTransactionPending

* eth: Implement Trace_Call, remove traceTxPending

* eth: debug_call -> debug_traceCall, recompute tx environment if pruned

* eth: fix nil panic

* eth: improve block retrieving logic in tracers

* internal/web3ext: add debug_traceCall to console
2020-09-07 10:52:01 +02:00
Péter Szilágyi
f86324edb7 Merge pull request #21504 from karalabe/trie-path-sync
core, eth, trie: prepare trie sync for path based operation
2020-09-02 13:52:51 +03:00
Péter Szilágyi
eeaf191633 core, eth, trie: prepare trie sync for path based operation 2020-09-02 13:21:32 +03:00
Martin Holst Swende
3010f9fc75 eth/downloader: change intial download size (#21366)
This changes how the downloader works, a little bit. Previously, when block sync started,
we immediately started filling up to 8192 blocks. Usually this is fine, blocks are small
in the early numbers. The threshold then is lowered as we measure the size of the blocks
that are filled.

However, if the node is shut down and restarts syncing while we're in a heavy segment,
that might be bad. This PR introduces a more conservative initial threshold of 2K blocks
instead.
2020-09-02 11:01:46 +02:00
ucwong
d90bbce954 go.mod : update goja dependency (#21432) 2020-09-01 12:56:22 +02:00
Giuseppe Bertone
5cdb476dd1 "Downloader queue stats" is now provided once per minute (#21455)
* "Downloader queue stats" is now a DEBUG information

I think this info is more a DEBUG related information then an INFO. If it must remains an INFO, maybe it can be slow down to one time every 5 minutes or so.

* Update queue.go

"Downloader queue stats" information is now provided once every minute instead of once every 10 seconds.
2020-09-01 11:02:12 +02:00
Hanjiang Yu
ff23e265cd internal: fix personal.sign() (#21503) 2020-09-01 10:23:04 +02:00
Fuyang Deng
12d8570322 accounts/abi: fix a bug in getTypeSize method (#21501)
* accounts/abi: fix a bug in getTypeSize method

e.g. for "Tuple[2]" type, the element of the array is a tuple type and the size of the tuple may not be 32.

* accounts/abi: add unit test of getTypeSize method
2020-09-01 07:29:54 +00:00
Felix Lange
5883afb3ef rpc: fix issue with null JSON-RPC messages (#21497) 2020-08-28 16:27:58 +02:00
libotony
05280a7ae3 eth/tracers: revert reason in call_tracer + error for failed internal calls (#21387)
* tests: add testdata of call tracer

* eth/tracers: return revert reason in call_tracer

* eth/tracers: regenerate assets

* eth/tracers: add error message even if no exec occurrs, fixes #21438

Co-authored-by: Martin Holst Swende <martin@swende.se>
2020-08-27 11:33:45 +02:00
Péter Szilágyi
d97e0063d5 Merge pull request #21491 from karalabe/state-sync-leak-fix
core/state, eth, trie: stabilize memory use, fix memory leak
2020-08-27 11:24:28 +03:00
ucwong
856307d8bb go.mod | goleveldb latest update (#21448)
* go.mod | goleveldb latest update

* go.mod update

* leveldb options

* go.mod: double check

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2020-08-26 15:53:12 +02:00
Marius van der Wijden
16d7eae1c8 eth: updated comments (#21490) 2020-08-26 13:20:12 +03:00
Péter Szilágyi
d8da0b3d81 core/state, eth, trie: stabilize memory use, fix memory leak 2020-08-26 13:05:06 +03:00
Marius van der Wijden
92b12ee6c6 accounts/abi/bind/backends: Disallow AdjustTime for non-empty blocks (#21334)
* accounts/abi/bind/backends: Disallow timeshift for non-empty blocks

* accounts/abi/bind/backends: added tests for adjust time

* accounts/abi/bind/simulated: added comments, fixed test for AdjustTime

* accounts/abi/bind/backends: updated comment
2020-08-26 09:37:00 +02:00
Felix Lange
fc20680b95 params: begin v1.9.21 release cycle 2020-08-25 16:21:41 +02:00
Felix Lange
979fc96899 params: release Geth v1.9.20 2020-08-25 16:20:37 +02:00
Péter Szilágyi
63a9d4b2ae Merge pull request #21486 from karalabe/cht-1.9.20
params: update CHTs for v1.9.20 release
2020-08-25 13:25:23 +03:00
Péter Szilágyi
ce5f94920d params: update CHTs for v1.9.20 release 2020-08-25 13:02:51 +03:00
Shude Li
341f451083 graphql: add support for retrieving the chain id (#21451) 2020-08-25 11:38:56 +03:00
Péter Szilágyi
d13b8e5570 Merge pull request #21483 from karalabe/freezer-truncate-silent
core/rawdb: only complain loudly if truncating many items
2020-08-25 09:21:44 +03:00
Péter Szilágyi
5655dce3b8 core/rawdb: only complain loudly if truncating many items 2020-08-25 09:03:14 +03:00
timcooijmans
7b5107b73f p2p/discover: avoid dropping unverified nodes when table is almost empty (#21396)
This change improves discovery behavior in small networks. Very small
networks would often fail to bootstrap because all member nodes were
dropping table content due to findnode failure. The check is now changed
to avoid dropping nodes on findnode failure when their bucket is almost
empty. It also relaxes the liveness check requirement for FINDNODE/v4
response nodes, returning unverified nodes as results when there aren't
any verified nodes yet.

The "findnode failed" log now reports whether the node was dropped
instead of the number of results. The value of the "results" was
always zero by definition.

Co-authored-by: Felix Lange <fjl@twurst.com>
2020-08-24 14:42:39 +02:00
Péter Szilágyi
bdde616f23 Merge pull request #21477 from karalabe/snapshotter-shallow-generator
core/state/snapshot: reduce disk layer depth during generation
2020-08-24 14:00:57 +03:00
Péter Szilágyi
3ee91b9f2e core/state/snapshot: reduce disk layer depth during generation 2020-08-24 13:22:36 +03:00
Martin Holst Swende
0f4e7c9b0d eth: utilize sync bloom for getNodeData (#21445)
* eth/downloader, eth/handler: utilize sync bloom for getNodeData

* trie: handle if bloom is nil

* trie, downloader: check bloom nilness externally
2020-08-24 11:32:12 +03:00
Martin Holst Swende
1b5a867eec core: do less lookups when writing fast-sync block bodies (#21468) 2020-08-22 18:12:04 +02:00
gary rong
87c0ba9213 core, eth, les, trie: add a prefix to contract code (#21080) 2020-08-21 15:10:40 +03:00
Péter Szilágyi
b68929caee Merge pull request #21472 from holiman/fix_dltest_fail
eth/downloader: fix rollback issue on short chains
2020-08-21 14:43:14 +03:00
Martin Holst Swende
9f7b79af00 eth/downloader: fix rollback issue on short chains 2020-08-21 13:36:08 +02:00
Marius van der Wijden
4e54b1a45e metrics: zero temp variable in updateMeter (#21470)
* metrics: zero temp variable in  updateMeter

Previously the temp variable was not updated properly after summing it to count.
This meant we had astronomically high metrics, now we zero out the temp whenever we
sum it onto the snapshot count

* metrics: move temp variable to be aligned, unit tests

Moves the temp variable in MeterSnapshot to be 64-bit aligned because of the atomic bug.
Adds a unit test, that catches the previous bug.
2020-08-21 11:04:36 +03:00
Péter Szilágyi
a70a79b285 Merge pull request #21466 from karalabe/go1.15
travis, dockerfile, appveyor, build: bump to Go 1.15
2020-08-20 17:41:26 +03:00
Péter Szilágyi
15fdaf2005 travis, dockerfile, appveyor, build: bump to Go 1.15 2020-08-20 16:41:37 +03:00
Péter Szilágyi
8cbdc8638f core: define and test chain rewind corner cases (#21409)
* core: define and test chain reparation cornercases

* core: write up a variety of set-head tests

* core, eth: unify chain rollbacks, handle all the cases

* core: make linter smile

* core: remove commented out legacy code

* core, eth/downloader: fix review comments

* core: revert a removed recovery mechanism
2020-08-20 13:01:24 +03:00
Marius van der Wijden
0bdd295cc0 core: more detailed metering for reorgs (#21420) 2020-08-20 09:49:35 +02:00
Martin Holst Swende
7ebc6c43ff cmd/evm: statet8n output folder + tx hashes on trace filenames (#21406)
* t8ntool: add output basedir

* t8ntool: add txhash to trace filename

* t8ntool: don't default to '.' basedir, allow absolute paths
2020-08-19 11:31:13 +02:00
Péter Szilágyi
560d44479c Merge pull request #21461 from karalabe/ppa-drop-disco
build: drop disco, enable groovy on Ubuntu PPAs
2020-08-19 10:29:54 +03:00
Péter Szilágyi
32b078d418 build: drop disco, enable groovy on Ubuntu PPAs 2020-08-19 10:28:08 +03:00
Giuseppe Bertone
2ff464b29d core/state: fixed some comments (#21450) 2020-08-19 09:54:21 +03:00
Marius van der Wijden
f3bafecef7 metrics: make meter updates lock-free (#21446) 2020-08-18 11:27:04 +02:00
Martin Holst Swende
54add42550 cmd/geth/tests: try to fix spurious travis failure in les tests (#21410)
* cmd/geth/tests: try to fix spurious travis failure in les tests

* cmd/geth: les_test - remove extraneous option during boot
2020-08-14 14:18:12 +02:00
Péter Szilágyi
04926db204 params: begin v1.9.20 release cycle 2020-08-11 14:11:16 +03:00
Péter Szilágyi
3e0641923d params: release Geth v1.9.19 2020-08-11 14:10:21 +03:00
Péter Szilágyi
74925e547f Merge pull request #21437 from karalabe/cht-1.9.19
params: update CHTs for v1.9.19
2020-08-11 10:34:08 +03:00
Péter Szilágyi
7afdf792ab params: update CHTs for v1.9.19 2020-08-11 10:20:03 +03:00
Martin Holst Swende
c28fd9c079 tests: add Berlin-definition identical to YOLOv1 (#21435) 2020-08-10 21:06:14 +02:00
Péter Szilágyi
4baa574410 Merge pull request #21434 from karalabe/ethstats-split-rwlock
ethstats: split read and write lock, otherwise they'll lock up
2020-08-10 15:13:31 +03:00
Péter Szilágyi
9f45d6efae ethstats: split read and write lock, otherwise they'll lock up 2020-08-10 14:33:22 +03:00
Péter Szilágyi
cbbc54c495 Merge pull request #21433 from holiman/statsync_exiter
eth/downloader: allow all timers to exit
2020-08-10 12:08:46 +03:00
Martin Holst Swende
7cee2509c0 eth/downloader: allow all timers to exit 2020-08-10 10:42:33 +02:00
Péter Szilágyi
48b484c5ac Merge pull request #21428 from holiman/ethstats_moar
ethstats: overwrite old errors
2020-08-07 19:40:28 +03:00
Péter Szilágyi
06125bff89 Merge pull request #21429 from holiman/timerfix
eth/downloader: set deliverytime on drops and timeouts too
2020-08-07 16:36:33 +03:00
Martin Holst Swende
9fea1a5cf5 eth/downloader: set deliverytime on drops and timeouts too 2020-08-07 15:34:58 +02:00
gary rong
e401f5ff10 les: close all connected les-server when shutdown (#21426)
* les: close all connected les-server when shutdown

* les: linter nitpick

Co-authored-by: Martin Holst Swende <martin@swende.se>
2020-08-07 15:33:00 +02:00
Martin Holst Swende
6a53ce29a4 ethstats: overwrite old errors 2020-08-07 14:44:44 +02:00
Péter Szilágyi
8f24097836 Merge pull request #21427 from karalabe/fix-statesync-delivery-time
eth/downloader: save the correct delivery time for state sync
2020-08-07 15:27:00 +03:00
Péter Szilágyi
4b9c0ea76d eth/downloader: save the correct delivery time for state sync 2020-08-07 15:17:13 +03:00
Péter Szilágyi
3bb8a4ed3f Merge pull request #21425 from holiman/leslock
les: update checktime even if check fails
2020-08-07 12:32:01 +03:00
Martin Holst Swende
983cb25a07 les: update checktime even if check fails 2020-08-07 10:57:02 +02:00
Péter Szilágyi
68754f3931 cmd/utils: grant snapshot cache to trie if disabled (#21416)
* cmd/utils: grant snapshot cache to trie if disabled

* eth: fix up default non-mainnet cache distribution
2020-08-06 15:28:31 +03:00
timcooijmans
5d4512b113 eth: use maxQueuedTxAnns for to limit the number of transactions announced (#21419) 2020-08-06 15:19:00 +03:00
rene
d21303f9dd cmd/geth: fixes db unavailability for chain commands (#21415)
* chaincmd should make config nodes instead of full nodes

* add documentation for using makeConfigNode instead of makeFullNode;

* add documentation to functions

* code style
2020-08-06 10:24:36 +03:00
Péter Szilágyi
4fde0cabc1 Merge pull request #21411 from holiman/fix_codelookup
core/vm: avoid map lookups for accessing jumpdest analysis
2020-08-06 08:09:15 +03:00
rene
4a04127ce3 cmd/geth: fix import / export issues related to DB unavailability (#21414)
* should fix import / export issues related to DB unavailability

* document reason for makeConfigNode

* fix comment

* comment consistency

* remove comments

* lint
2020-08-06 08:02:05 +03:00
rene
2de37f28e0 downloader: add eth65 tests (#21383)
* eth65 tests

linted

* remove non-latest eth light tests
2020-08-05 12:22:29 +03:00
Robert Zaremba
5a88a7cf5b core: use errors.Is for consensus errors check (#21095) 2020-08-05 09:52:54 +02:00
Felix Lange
1d25039ff5 p2p/nat: limit UPNP request concurrency (#21390)
This adds a lock around requests because some routers can't handle
concurrent requests. Requests are also rate-limited.
 
The Map function request a new mapping exactly when the map timeout
occurs instead of 5 minutes earlier. This should prevent duplicate mappings.
2020-08-05 09:51:37 +02:00
Martin Holst Swende
8ead45c20b core/vm: avoid map lookups for accessing jumpdest analysis 2020-08-04 15:45:35 +02:00
Martin Holst Swende
82a9e11058 ethstats: avoid concurrent write on websocket (#21404)
Fixes #21403
2020-08-04 12:21:51 +02:00
Hao Duan
b35e4fce99 core: avoid modification of accountSet cache in tx_pool (#21159)
* core: avoid modification of accountSet cache in tx_pool

when runReorg, we may copy the dirtyAccounts' accountSet cache to promoteAddrs
in which accounts will be promoted, however, if we have reset request at the
same time, we may reuse promoteAddrs and modify the cache content which is
against the original intention of accountSet cache. So, we need to make a new
slice here to avoid modify accountSet cache.

* core: fix flatten condition + comment

Co-authored-by: Felix Lange <fjl@twurst.com>
2020-08-04 11:51:53 +02:00
Adam Schmideg
e24e05dd01 cmd/devp2p: print enode:// URL in enrdump (#21270)
Co-authored-by: Felix Lange <fjl@twurst.com>
2020-08-04 11:33:07 +02:00
Natsu Kagami
90dedea40f signer: EIP 712, parse bytes and bytesX as hex strings + correct padding (#21307)
* Handle hex strings for bytesX types

* Add tests for parseBytes

* Improve tests

* Return nil bytes if error is non-nil

* Right-pad instead of left-pad bytes

* More tests
2020-08-03 21:53:12 +02:00
rene
c0c01612e9 node: refactor package node (#21105)
This PR significantly changes the APIs for instantiating Ethereum nodes in
a Go program. The new APIs are not backwards-compatible, but we feel that
this is made up for by the much simpler way of registering services on
node.Node. You can find more information and rationale in the design
document: https://gist.github.com/renaynay/5bec2de19fde66f4d04c535fd24f0775.

There is also a new feature in Node's Go API: it is now possible to
register arbitrary handlers on the user-facing HTTP server. In geth, this
facility is used to enable GraphQL.

There is a single minor change relevant for geth users in this PR: The
GraphQL API is no longer available separately from the JSON-RPC HTTP
server. If you want GraphQL, you need to enable it using the
./geth --http --graphql flag combination.

The --graphql.port and --graphql.addr flags are no longer available.
2020-08-03 19:40:46 +02:00
Natsu Kagami
b2b14e6ce3 signer/core: EIP-712 encoded data should not reject a Domain without a ChainId (#21306)
* Do not check for a non-nil ChainId

* Add encoding test
2020-08-03 15:30:32 +02:00
rene
290d6bd903 rpc: add SetHeader method to Client (#21392)
Resolves #20163

Co-authored-by: Felix Lange <fjl@twurst.com>
2020-08-03 14:08:42 +02:00
Felix Lange
9c2ac6fbd5 rpc: remove silly use of ReadVarint in subscription ID generator (#21391)
Found by @protolambda
2020-07-31 16:20:31 +02:00
Péter Szilágyi
a00dc5095b Merge pull request #21358 from hendrikhofstadt/fix/tx-sort-time
core: sort txs at the same gas price by received time
2020-07-30 10:23:36 +03:00
meowsbits
ff90894636 core/rawdb: convert some comments to godoc convention (#21384) 2020-07-29 21:53:59 +02:00
ucwong
9e04c5ec83 core/bloombits: use single channel for shutdown (#20878)
This replaces the two-stage shutdown scheme with the one we
use almost everywhere else: a single quit channel signalling
termination.

Co-authored-by: Felix Lange <fjl@twurst.com>
2020-07-29 13:49:12 +02:00
Julian Y
abf2d7d74f build: use -trimpath flag when building executables (#21374)
* Disable symbol table and DWARF generation by default.
Trimpath if compiling with Go >= 1.13

* Set Go to minimum version 1.13. Revert debug symbol changes.
2020-07-29 13:48:03 +03:00
rene
1976bb3df0 eth/downloader: remove eth62 (#21378)
* init

notes

removed some mentions of eth62, bumped protocol err too old to >=63

* remove sanity checks and bump supported protocol version up to 63

* remove 62 tests, still need to add 65

* remove 65 tests
2020-07-29 13:47:19 +03:00
gary rong
350a0490ab les: fix unittest (#21382) 2020-07-29 13:44:14 +03:00
Robert Zaremba
37564ceda6 miner: refactor helper functions in worker.go (#21044)
This reduces complexity of some lengthy functions in worker.go,
making the code easier to read.
2020-07-28 18:16:49 +02:00
gary rong
28c5a8a54b les: implement new les fetcher (#20692)
* cmd, consensus, eth, les: implement light fetcher

* les: address comment

* les: address comment

* les: address comments

* les: check td after delivery

* les: add linearExpiredValue for error counter

* les: fix import

* les: fix dead lock

* les: order announces by td

* les: encapsulate invalid counter

* les: address comment

* les: add more checks during the delivery

* les: fix log

* eth, les: fix lint

* eth/fetcher: address comment
2020-07-28 18:02:35 +03:00
Péter Szilágyi
298a19bbc6 core: API-less transaction time sorting 2020-07-28 17:13:40 +03:00
Hendrik Hofstadt
c47052a580 core: sort txs at the same gas price by received time 2020-07-28 17:13:39 +03:00
gary rong
93da0cf8a1 cmd, core, eth, light, trie: dump clean cache periodically (#20391)
* cmd, core, eth, light, trie: dump clean cache periodically

* eth: update config

* trie: minor fix

* core, trie: address comments

* eth: remove useless

* trie: print clean cache dump start too

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2020-07-28 16:30:31 +03:00
6543
79ce5537ab signer/storage: fix a badly ordered error check (#21379) 2020-07-28 12:47:05 +03:00
Péter Szilágyi
8e7bee9b56 params: begin v1.9.19 release cycle 2020-07-27 14:58:45 +03:00
Péter Szilágyi
f538259187 params: release Geth v1.9.18 2020-07-27 14:53:53 +03:00
gary rong
b1be979443 params: upgrade CHTs (#21376) 2020-07-27 12:57:15 +03:00
Péter Szilágyi
e997f92caf Merge pull request #21368 from holiman/update_uint256
deps: update uint256 to v1.1.1
2020-07-24 15:02:52 +03:00
Martin Holst Swende
56434bfa89 deps: update uint256 to v1.1.1 2020-07-24 14:00:08 +02:00
Péter Szilágyi
6793ffa12b Merge pull request #21300 from rjl493456442/txpool-fix-queued-evictions
core: fix queued transaction eviction
2020-07-24 11:14:42 +03:00
rjl493456442
5413df1dfa core: fix heartbeat in txpool
core: address comment
2020-07-24 11:12:59 +03:00
villanuevawill
c374447401 core: fix queued transaction eviction
Solves issue#20582. Non-executable transactions should not be evicted on each tick if there are no promote transactions or if a pending/reset empties the pending list. Tests and logging expanded to handle these cases in the future.

core/tx_pool: use a ts for each tx in the queue, but only update the heartbeat on promotion or pending replaced

queuedTs proper naming
2020-07-24 11:11:57 +03:00
Martin Holst Swende
105922180f eth/downloader: refactor downloader + queue (#21263)
* eth/downloader: refactor downloader + queue

downloader, fetcher: throttle-metrics, fetcher filter improvements, standalone resultcache

downloader: more accurate deliverytime calculation, less mem overhead in state requests

downloader/queue: increase underlying buffer of results, new throttle mechanism

eth/downloader: updates to tests

eth/downloader: fix up some review concerns

eth/downloader/queue: minor fixes

eth/downloader: minor fixes after review call

eth/downloader: testcases for queue.go

eth/downloader: minor change, don't set progress unless progress...

eth/downloader: fix flaw which prevented useless peers from being dropped

eth/downloader: try to fix tests

eth/downloader: verify non-deliveries against advertised remote head

eth/downloader: fix flaw with checking closed-status causing hang

eth/downloader: hashing avoidance

eth/downloader: review concerns + simplify resultcache and queue

eth/downloader: add back some locks, address review concerns

downloader/queue: fix remaining lock flaw

* eth/downloader: nitpick fixes

* eth/downloader: remove the *2*3/4 throttling threshold dance

* eth/downloader: print correct throttle threshold in stats

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2020-07-24 10:46:26 +03:00
Felix Lange
3a57eecc69 mobile: fix build on iOS (#21362)
This fixes the iOS framework build by naming the second parameter of the
Signer interface method. The name is important because it becomes part
of the objc method signature.

Fixes #21340
2020-07-23 19:15:40 +02:00
Felix Lange
997b55236e build: fix GOBIN for gomobile commands (#21361) 2020-07-23 12:34:08 +02:00
meowsbits
4c268e65a0 cmd/utils: implement configurable developer (--dev) account options (#21301)
* geth,utils: implement configurable developer account options

Prior to this change --dev (developer) mode
generated one account with an empty password,
irrespective of existing --password and --miner.etherbase
options.

This change makes --dev mode compatible with these
existing flags.

--dev mode may now be used in conjunction with
--password and --miner.etherbase flags to configure
the developer faucet using an existing keystore or
in creating a new account.

Signed-off-by: meows <b5c6@protonmail.com>

* main: remove key/pass flags from usage developer section

These flags are included already in other sections,
and it is not desired to duplicate them.

They were originally included in this section
along with added support for these flags in the
developer mode.

Signed-off-by: meows <b5c6@protonmail.com>
2020-07-23 06:47:34 +03:00
Péter Szilágyi
0b53e485d8 Merge pull request #21352 from karalabe/dev-noinit-genesis
cmd/utils: reuse existing genesis in persistent dev mode
2020-07-22 17:39:08 +03:00
Péter Szilágyi
9e22e912e3 cmd/utils: reuse existing genesis in persistent dev mode 2020-07-21 15:58:29 +03:00
rene
123864fc05 whisper/whisperv6: improve test error messages (#21348) 2020-07-21 10:53:06 +02:00
Sammy Libre
7163a6664e ethclient: serialize negative block number as "pending" (#21177)
Fixes #21175

Co-authored-by: sammy007 <sammy007@users.noreply.github.com>
Co-authored-by: Adam Schmideg <adamschmideg@users.noreply.github.com>
2020-07-21 10:51:15 +02:00
Binacs
4366c45e4e les: make clientPool.connectedBias configurable (#21305) 2020-07-21 10:23:40 +02:00
Péter Szilágyi
3a52c4dcf2 Merge pull request #21336 from karalabe/tiny-ref-optimization
core/vm: use pointers to operations vs. copy by value
2020-07-21 10:53:12 +03:00
Péter Szilágyi
722b742780 params: begin v1.9.18 release cycle 2020-07-20 15:58:33 +03:00
Péter Szilágyi
748f22c192 params: release Geth v1.9.17 2020-07-20 15:56:42 +03:00
gary rong
43e2e58cbd accounts, internal: fix funding check when estimating gas (#21346)
* internal, accounts: fix funding check when estimate gas

* accounts, internal: address comments
2020-07-20 15:52:42 +03:00
Péter Szilágyi
35ddf36229 Merge pull request #21347 from karalabe/ethstats-fixes
ethstats: fix reconnection issue, implement primus pings
2020-07-20 11:47:13 +03:00
Péter Szilágyi
0fef66c739 ethstats: fix reconnection issue, implement primus pings 2020-07-20 11:46:41 +03:00
Péter Szilágyi
508891e64b core/vm: use pointers to operations vs. copy by value 2020-07-16 15:32:01 +03:00
Nikola Madjarevic
9e88224eb8 core: raise gas limit in --dev mode, seed blake precompile (#21323)
* Set gasLimit in --dev mode to be 9m.

* core: Set gasLimit to 11.5 milion and add 1 wei allocation for BLAKE2b
2020-07-16 15:08:38 +03:00
Martin Holst Swende
295693759e core/vm: less allocations for various call variants (#21222)
* core/vm/runtime/tests: add more benchmarks

* core/vm: initial work on improving alloc count for calls to precompiles

name                                  old time/op    new time/op    delta
SimpleLoop/identity-precompile-10M-6     117ms ±75%      43ms ± 1%  -63.09%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   79.6ms ± 4%    70.5ms ± 1%  -11.42%  (p=0.008 n=5+5)

name                                  old alloc/op   new alloc/op   delta
SimpleLoop/identity-precompile-10M-6    24.4MB ± 0%     4.9MB ± 0%  -79.94%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   13.2kB ± 0%    13.2kB ± 0%     ~     (p=0.357 n=5+5)

name                                  old allocs/op  new allocs/op  delta
SimpleLoop/identity-precompile-10M-6      382k ± 0%      153k ± 0%  -59.99%  (p=0.000 n=5+4)
SimpleLoop/loop-10M-6                     40.0 ± 0%      40.0 ± 0%     ~     (all equal)

* core/vm: don't allocate big.int for touch

name                                  old time/op    new time/op    delta
SimpleLoop/identity-precompile-10M-6    43.3ms ± 1%    42.4ms ± 7%     ~     (p=0.151 n=5+5)
SimpleLoop/loop-10M-6                   70.5ms ± 1%    76.7ms ± 1%   +8.67%  (p=0.008 n=5+5)

name                                  old alloc/op   new alloc/op   delta
SimpleLoop/identity-precompile-10M-6    4.90MB ± 0%    2.46MB ± 0%  -49.83%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   13.2kB ± 0%    13.2kB ± 1%     ~     (p=0.571 n=5+5)

name                                  old allocs/op  new allocs/op  delta
SimpleLoop/identity-precompile-10M-6      153k ± 0%       76k ± 0%  -49.98%  (p=0.029 n=4+4)
SimpleLoop/loop-10M-6                     40.0 ± 0%      40.0 ± 0%     ~     (all equal)

* core/vm: reduce allocs in staticcall

name                                  old time/op    new time/op    delta
SimpleLoop/identity-precompile-10M-6    42.4ms ± 7%    37.5ms ± 6%  -11.68%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   76.7ms ± 1%    69.1ms ± 1%   -9.82%  (p=0.008 n=5+5)

name                                  old alloc/op   new alloc/op   delta
SimpleLoop/identity-precompile-10M-6    2.46MB ± 0%    0.02MB ± 0%  -99.35%  (p=0.008 n=5+5)
SimpleLoop/loop-10M-6                   13.2kB ± 1%    13.2kB ± 0%     ~     (p=0.143 n=5+5)

name                                  old allocs/op  new allocs/op  delta
SimpleLoop/identity-precompile-10M-6     76.4k ± 0%      0.1k ± 0%     ~     (p=0.079 n=4+5)
SimpleLoop/loop-10M-6                     40.0 ± 0%      40.0 ± 0%     ~     (all equal)

* trie: better use of hasher keccakState

* core/state/statedb: reduce allocations in getDeletedStateObject

* core/vm: reduce allocations in all call derivates

* core/vm: reduce allocations in call variants

- Make returnstack `uint32`
- Use a `sync.Pool` of `stack`s

* core/vm: fix tests

* core/vm: goimports

* core/vm: tracer fix + staticcall gas fix

* core/vm: add back snapshot to staticcall

* core/vm: review concerns + make returnstack pooled + enable returndata in traces

* core/vm: fix some test tracer method signatures

* core/vm: run gencodec, minor comment polish

Co-authored-by: Péter Szilágyi <peterke@gmail.com>
2020-07-16 15:06:19 +03:00
Guillaume Ballet
240d1851db trie: quell linter in commiter.go (#21329) 2020-07-15 11:00:04 +03:00
Martin Holst Swende
6c9f040ebe core: transaction pool optimizations (#21328)
* core: added local tx pool test case

* core, crypto: various allocation savings regarding tx handling

* core/txlist, txpool: save a reheap operation, avoid some bigint allocs

Co-authored-by: Marius van der Wijden <m.vanderwijden@live.de>
2020-07-14 21:42:32 +02:00
rene
5b081ab214 cmd/clef: change --rpcport to --http.port and update flags in docs (#21318) 2020-07-14 10:35:32 +02:00
Felix Lange
6ef4495a8f p2p/discover: require table nodes to have an IP (#21330)
This fixes a corner case in discv5. The issue cannot happen in discv4
because it performs IP checks on all incoming node information.
2020-07-13 22:25:45 +02:00
gary rong
79addac698 core/rawdb: better log messages for ancient failure (#21327) 2020-07-13 20:43:30 +02:00
rene
7d5267e3a2 .github: Change Code Owners (#21326)
* modify code owners

* add marius
2020-07-13 16:20:20 +02:00
gary rong
4edbc1f2bb internal/ethapi: cap txfee for SignTransaction and Resend (#21231) 2020-07-13 12:45:39 +02:00
Tien
6cf6e1d753 README.md: point Go API reference link to pkg.go.dev (#21321) 2020-07-13 11:22:30 +02:00
libotony
2e08dad9e6 p2p/discv5: unset pingEcho on pong timeout (#21324) 2020-07-13 11:20:47 +02:00
Felix Lange
af258efdb9 light: goimports -w (#21325) 2020-07-13 11:17:49 +02:00
gary rong
6eef141aef les: historical data garbage collection (#19570)
This change introduces garbage collection for the light client. Historical
chain data is deleted periodically. If you want to disable the GC, use
the --light.nopruning flag.
2020-07-13 11:02:54 +02:00
Felix Lange
b8dd0890b3 params: begin v1.9.17 release cycle 2020-07-10 12:40:31 +02:00
276 changed files with 13618 additions and 14053 deletions

11
.github/CODEOWNERS vendored
View File

@@ -3,21 +3,20 @@
accounts/usbwallet @karalabe
accounts/scwallet @gballet
accounts/abi @gballet
accounts/abi @gballet @MariusVanDerWijden
cmd/clef @holiman
cmd/puppeth @karalabe
consensus @karalabe
core/ @karalabe @holiman @rjl493456442
dashboard/ @kurkomisi
eth/ @karalabe @holiman @rjl493456442
graphql/ @gballet
les/ @zsfelfoldi @rjl493456442
light/ @zsfelfoldi @rjl493456442
mobile/ @karalabe @ligi
node/ @fjl @renaynay
p2p/ @fjl @zsfelfoldi
rpc/ @fjl @holiman
p2p/simulations @zelig @janos @justelad
p2p/protocols @zelig @janos @justelad
p2p/testing @zelig @janos @justelad
p2p/simulations @fjl
p2p/protocols @fjl
p2p/testing @fjl
signer/ @holiman
whisper/ @gballet @gluk256

View File

@@ -16,7 +16,7 @@ jobs:
- stage: lint
os: linux
dist: xenial
go: 1.14.x
go: 1.15.x
env:
- lint
git:
@@ -34,12 +34,22 @@ jobs:
- go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES
- stage: build
os: linux
dist: xenial
go: 1.14.x
env:
- GO111MODULE=on
script:
- go run build/ci.go install
- go run build/ci.go test -coverage $TEST_PACKAGES
# These are the latest Go versions.
- stage: build
os: linux
arch: amd64
dist: xenial
go: 1.14.x
go: 1.15.x
env:
- GO111MODULE=on
script:
@@ -51,7 +61,7 @@ jobs:
os: linux
arch: arm64
dist: xenial
go: 1.14.x
go: 1.15.x
env:
- GO111MODULE=on
script:
@@ -61,7 +71,7 @@ jobs:
- stage: build
os: osx
osx_image: xcode11.3
go: 1.14.x
go: 1.15.x
env:
- GO111MODULE=on
script:
@@ -82,7 +92,7 @@ jobs:
if: type = push
os: linux
dist: xenial
go: 1.14.x
go: 1.15.x
env:
- ubuntu-ppa
- GO111MODULE=on
@@ -99,7 +109,7 @@ jobs:
- python-paramiko
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 -goversion 1.14.2 -upload ethereum/ethereum -sftp-user geth-ci -signer "Go Ethereum Linux Builder <geth-ci@ethereum.org>"
- go run build/ci.go debsrc -goversion 1.15 -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
@@ -107,7 +117,7 @@ jobs:
os: linux
dist: xenial
sudo: required
go: 1.14.x
go: 1.15.x
env:
- azure-linux
- GO111MODULE=on
@@ -144,7 +154,7 @@ jobs:
dist: xenial
services:
- docker
go: 1.14.x
go: 1.15.x
env:
- azure-linux-mips
- GO111MODULE=on
@@ -192,7 +202,7 @@ jobs:
git:
submodules: false # avoid cloning ethereum/tests
before_install:
- curl https://dl.google.com/go/go1.14.2.linux-amd64.tar.gz | tar -xz
- curl https://dl.google.com/go/go1.15.linux-amd64.tar.gz | tar -xz
- export PATH=`pwd`/go/bin:$PATH
- export GOROOT=`pwd`/go
- export GOPATH=$HOME/go
@@ -210,7 +220,7 @@ jobs:
- stage: build
if: type = push
os: osx
go: 1.14.x
go: 1.15.x
env:
- azure-osx
- azure-ios
@@ -242,7 +252,7 @@ jobs:
if: type = cron
os: linux
dist: xenial
go: 1.14.x
go: 1.15.x
env:
- azure-purge
- GO111MODULE=on

View File

@@ -1,5 +1,5 @@
# Build Geth in a stock Go builder container
FROM golang:1.14-alpine as builder
FROM golang:1.15-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git

View File

@@ -1,5 +1,5 @@
# Build Geth in a stock Go builder container
FROM golang:1.14-alpine as builder
FROM golang:1.15-alpine as builder
RUN apk add --no-cache make gcc musl-dev linux-headers git

View File

@@ -4,7 +4,7 @@ Official Golang implementation of the Ethereum protocol.
[![API Reference](
https://camo.githubusercontent.com/915b7be44ada53c290eb157634330494ebe3e30a/68747470733a2f2f676f646f632e6f72672f6769746875622e636f6d2f676f6c616e672f6764646f3f7374617475732e737667
)](https://godoc.org/github.com/ethereum/go-ethereum)
)](https://pkg.go.dev/github.com/ethereum/go-ethereum?tab=doc)
[![Go Report Card](https://goreportcard.com/badge/github.com/ethereum/go-ethereum)](https://goreportcard.com/report/github.com/ethereum/go-ethereum)
[![Travis](https://travis-ci.org/ethereum/go-ethereum.svg?branch=master)](https://travis-ci.org/ethereum/go-ethereum)
[![Discord](https://img.shields.io/badge/discord-join%20chat-blue.svg)](https://discord.gg/nthXNEv)
@@ -108,7 +108,7 @@ accounts available between them.*
### Full node on the Rinkeby test network
Go Ethereum also supports connecting to the older proof-of-authority based test network
Go Ethereum also supports connecting to the older proof-of-authority based test network
called [*Rinkeby*](https://www.rinkeby.io) which is operated by members of the community.
```shell
@@ -117,10 +117,10 @@ $ geth --rinkeby console
### Full node on the Ropsten test network
In addition to Görli and Rinkeby, Geth also supports the ancient Ropsten testnet. The
In addition to Görli and Rinkeby, Geth also supports the ancient Ropsten testnet. The
Ropsten test network is based on the Ethash proof-of-work consensus algorithm. As such,
it has certain extra overhead and is more susceptible to reorganization attacks due to the
network's low difficulty/security.
network's low difficulty/security.
```shell
$ geth --ropsten console

View File

@@ -45,7 +45,7 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
// This nil assignment ensures at compile time that SimulatedBackend implements bind.ContractBackend.
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
var (
@@ -55,7 +55,7 @@ var (
)
// SimulatedBackend implements bind.ContractBackend, simulating a blockchain in
// the background. Its main purpose is to allow easily testing contract bindings.
// the background. Its main purpose is to allow for easy testing of contract bindings.
// Simulated backend implements the following interfaces:
// ChainReader, ChainStateReader, ContractBackend, ContractCaller, ContractFilterer, ContractTransactor,
// DeployBackend, GasEstimator, GasPricer, LogFilterer, PendingContractCaller, TransactionReader, and TransactionSender
@@ -123,10 +123,10 @@ func (b *SimulatedBackend) Rollback() {
func (b *SimulatedBackend) rollback() {
blocks, _ := core.GenerateChain(b.config, b.blockchain.CurrentBlock(), ethash.NewFaker(), b.database, 1, func(int, *core.BlockGen) {})
statedb, _ := b.blockchain.State()
stateDB, _ := b.blockchain.State()
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database(), nil)
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
}
// stateByBlockNumber retrieves a state by a given blocknumber.
@@ -146,12 +146,12 @@ func (b *SimulatedBackend) CodeAt(ctx context.Context, contract common.Address,
b.mu.Lock()
defer b.mu.Unlock()
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
return statedb.GetCode(contract), nil
return stateDB.GetCode(contract), nil
}
// BalanceAt returns the wei balance of a certain account in the blockchain.
@@ -159,12 +159,12 @@ func (b *SimulatedBackend) BalanceAt(ctx context.Context, contract common.Addres
b.mu.Lock()
defer b.mu.Unlock()
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
return statedb.GetBalance(contract), nil
return stateDB.GetBalance(contract), nil
}
// NonceAt returns the nonce of a certain account in the blockchain.
@@ -172,12 +172,12 @@ func (b *SimulatedBackend) NonceAt(ctx context.Context, contract common.Address,
b.mu.Lock()
defer b.mu.Unlock()
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return 0, err
}
return statedb.GetNonce(contract), nil
return stateDB.GetNonce(contract), nil
}
// StorageAt returns the value of key in the storage of an account in the blockchain.
@@ -185,17 +185,17 @@ func (b *SimulatedBackend) StorageAt(ctx context.Context, contract common.Addres
b.mu.Lock()
defer b.mu.Unlock()
statedb, err := b.stateByBlockNumber(ctx, blockNumber)
stateDB, err := b.stateByBlockNumber(ctx, blockNumber)
if err != nil {
return nil, err
}
val := statedb.GetState(contract, key)
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) {
func (b *SimulatedBackend) TransactionReceipt(_ context.Context, txHash common.Hash) (*types.Receipt, error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -207,7 +207,7 @@ func (b *SimulatedBackend) TransactionReceipt(ctx context.Context, txHash common
// blockchain. The isPending return value indicates whether the transaction has been
// mined yet. Note that the transaction may not be part of the canonical chain even if
// it's not pending.
func (b *SimulatedBackend) TransactionByHash(ctx context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
func (b *SimulatedBackend) TransactionByHash(_ context.Context, txHash common.Hash) (*types.Transaction, bool, error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -222,8 +222,8 @@ 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) {
// BlockByHash retrieves a block based on the block hash.
func (b *SimulatedBackend) BlockByHash(_ context.Context, hash common.Hash) (*types.Block, error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -250,7 +250,7 @@ func (b *SimulatedBackend) BlockByNumber(ctx context.Context, number *big.Int) (
// blockByNumberNoLock retrieves a block from the database by number, caching it
// (associated with its hash) if found without Lock.
func (b *SimulatedBackend) blockByNumberNoLock(ctx context.Context, number *big.Int) (*types.Block, error) {
func (b *SimulatedBackend) blockByNumberNoLock(_ context.Context, number *big.Int) (*types.Block, error) {
if number == nil || number.Cmp(b.pendingBlock.Number()) == 0 {
return b.blockchain.CurrentBlock(), nil
}
@@ -264,7 +264,7 @@ func (b *SimulatedBackend) blockByNumberNoLock(ctx context.Context, number *big.
}
// HeaderByHash returns a block header from the current canonical chain.
func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
func (b *SimulatedBackend) HeaderByHash(_ context.Context, hash common.Hash) (*types.Header, error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -282,7 +282,7 @@ func (b *SimulatedBackend) HeaderByHash(ctx context.Context, hash common.Hash) (
// 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) {
func (b *SimulatedBackend) HeaderByNumber(_ context.Context, block *big.Int) (*types.Header, error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -293,8 +293,8 @@ func (b *SimulatedBackend) HeaderByNumber(ctx context.Context, block *big.Int) (
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) {
// TransactionCount returns the number of transactions in a given block.
func (b *SimulatedBackend) TransactionCount(_ context.Context, blockHash common.Hash) (uint, error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -310,8 +310,8 @@ func (b *SimulatedBackend) TransactionCount(ctx context.Context, blockHash commo
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) {
// TransactionInBlock returns the transaction for a specific block at a specific index.
func (b *SimulatedBackend) TransactionInBlock(_ context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -338,7 +338,7 @@ func (b *SimulatedBackend) TransactionInBlock(ctx context.Context, blockHash com
}
// PendingCodeAt returns the code associated with an account in the pending state.
func (b *SimulatedBackend) PendingCodeAt(ctx context.Context, contract common.Address) ([]byte, error) {
func (b *SimulatedBackend) PendingCodeAt(_ context.Context, contract common.Address) ([]byte, error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -357,14 +357,14 @@ func newRevertError(result *core.ExecutionResult) *revertError {
}
}
// revertError is an API error that encompassas an EVM revertal with JSON error
// revertError is an API error that encompasses an EVM revert with JSON error
// code and a binary data blob.
type revertError struct {
error
reason string // revert reason hex encoded
}
// ErrorCode returns the JSON error code for a revertal.
// ErrorCode returns the JSON error code for a revert.
// See: https://github.com/ethereum/wiki/wiki/JSON-RPC-Error-Codes-Improvement-Proposal
func (e *revertError) ErrorCode() int {
return 3
@@ -383,11 +383,11 @@ func (b *SimulatedBackend) CallContract(ctx context.Context, call ethereum.CallM
if blockNumber != nil && blockNumber.Cmp(b.blockchain.CurrentBlock().Number()) != 0 {
return nil, errBlockNumberUnsupported
}
state, err := b.blockchain.State()
stateDB, err := b.blockchain.State()
if err != nil {
return nil, err
}
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), state)
res, err := b.callContract(ctx, call, b.blockchain.CurrentBlock(), stateDB)
if err != nil {
return nil, err
}
@@ -417,7 +417,7 @@ func (b *SimulatedBackend) PendingCallContract(ctx context.Context, call ethereu
// PendingNonceAt implements PendingStateReader.PendingNonceAt, retrieving
// the nonce currently pending for the account.
func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
func (b *SimulatedBackend) PendingNonceAt(_ context.Context, account common.Address) (uint64, error) {
b.mu.Lock()
defer b.mu.Unlock()
@@ -426,7 +426,7 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
// chain doesn't have miners, we just return a gas price of 1 for any call.
func (b *SimulatedBackend) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
func (b *SimulatedBackend) SuggestGasPrice(_ context.Context) (*big.Int, error) {
return big.NewInt(1), nil
}
@@ -448,7 +448,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
hi = b.pendingBlock.GasLimit()
}
// Recap the highest gas allowance with account's balance.
if call.GasPrice != nil && call.GasPrice.Uint64() != 0 {
if call.GasPrice != nil && call.GasPrice.BitLen() != 0 {
balance := b.pendingState.GetBalance(call.From) // from can't be nil
available := new(big.Int).Set(balance)
if call.Value != nil {
@@ -458,7 +458,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
available.Sub(available, call.Value)
}
allowance := new(big.Int).Div(available, call.GasPrice)
if hi > allowance.Uint64() {
if allowance.IsUint64() && hi > allowance.Uint64() {
transfer := call.Value
if transfer == nil {
transfer = new(big.Int)
@@ -525,7 +525,7 @@ func (b *SimulatedBackend) EstimateGas(ctx context.Context, call ethereum.CallMs
// callContract implements common code between normal and pending contract calls.
// state is modified during execution, make sure to copy it if necessary.
func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallMsg, block *types.Block, statedb *state.StateDB) (*core.ExecutionResult, error) {
func (b *SimulatedBackend) callContract(_ context.Context, call ethereum.CallMsg, block *types.Block, stateDB *state.StateDB) (*core.ExecutionResult, error) {
// Ensure message is initialized properly.
if call.GasPrice == nil {
call.GasPrice = big.NewInt(1)
@@ -537,23 +537,23 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
call.Value = new(big.Int)
}
// Set infinite balance to the fake caller account.
from := statedb.GetOrNewStateObject(call.From)
from := stateDB.GetOrNewStateObject(call.From)
from.SetBalance(math.MaxBig256)
// Execute the call.
msg := callmsg{call}
msg := callMsg{call}
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain, nil)
// Create a new environment which holds all relevant information
// about the transaction and calling mechanisms.
vmenv := vm.NewEVM(evmContext, statedb, b.config, vm.Config{})
gaspool := new(core.GasPool).AddGas(math.MaxUint64)
vmEnv := vm.NewEVM(evmContext, stateDB, b.config, vm.Config{})
gasPool := new(core.GasPool).AddGas(math.MaxUint64)
return core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
return core.NewStateTransition(vmEnv, msg, gasPool).TransitionDb()
}
// SendTransaction updates the pending block to include the given transaction.
// It panics if the transaction is invalid.
func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transaction) error {
func (b *SimulatedBackend) SendTransaction(_ context.Context, tx *types.Transaction) error {
b.mu.Lock()
defer b.mu.Unlock()
@@ -572,10 +572,10 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
}
block.AddTxWithChain(b.blockchain, tx)
})
statedb, _ := b.blockchain.State()
stateDB, _ := b.blockchain.State()
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database(), nil)
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
return nil
}
@@ -589,7 +589,7 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
// Block filter requested, construct a single-shot filter
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
} else {
// Initialize unset filter boundaried to run from genesis to chain head
// Initialize unset filter boundaries to run from genesis to chain head
from := int64(0)
if query.FromBlock != nil {
from = query.FromBlock.Int64()
@@ -607,15 +607,15 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
return nil, err
}
res := make([]types.Log, len(logs))
for i, log := range logs {
res[i] = *log
for i, nLog := range logs {
res[i] = *nLog
}
return res, nil
}
// SubscribeFilterLogs creates a background log filtering operation, returning a
// subscription immediately, which can be used to stream the found events.
func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
func (b *SimulatedBackend) SubscribeFilterLogs(_ context.Context, query ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
// Subscribe to contract events
sink := make(chan []*types.Log)
@@ -629,9 +629,9 @@ func (b *SimulatedBackend) SubscribeFilterLogs(ctx context.Context, query ethere
for {
select {
case logs := <-sink:
for _, log := range logs {
for _, nlog := range logs {
select {
case ch <- *log:
case ch <- *nlog:
case err := <-sub.Err():
return err
case <-quit:
@@ -647,8 +647,8 @@ 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) {
// SubscribeNewHead returns an event subscription for a new header.
func (b *SimulatedBackend) SubscribeNewHead(_ context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
// subscribe to a new head
sink := make(chan *types.Header)
sub := b.events.SubscribeNewHeads(sink)
@@ -675,20 +675,22 @@ func (b *SimulatedBackend) SubscribeNewHead(ctx context.Context, ch chan<- *type
}
// AdjustTime adds a time shift to the simulated clock.
// It can only be called on empty blocks.
func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
b.mu.Lock()
defer b.mu.Unlock()
if len(b.pendingBlock.Transactions()) != 0 {
return errors.New("Could not adjust time on non-empty block")
}
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)
}
block.OffsetTime(int64(adjustment.Seconds()))
})
statedb, _ := b.blockchain.State()
stateDB, _ := b.blockchain.State()
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), statedb.Database(), nil)
b.pendingState, _ = state.New(b.pendingBlock.Root(), stateDB.Database(), nil)
return nil
}
@@ -698,19 +700,19 @@ func (b *SimulatedBackend) Blockchain() *core.BlockChain {
return b.blockchain
}
// callmsg implements core.Message to allow passing it as a transaction simulator.
type callmsg struct {
// callMsg implements core.Message to allow passing it as a transaction simulator.
type callMsg struct {
ethereum.CallMsg
}
func (m callmsg) From() common.Address { return m.CallMsg.From }
func (m callmsg) Nonce() uint64 { return 0 }
func (m callmsg) CheckNonce() bool { return false }
func (m callmsg) To() *common.Address { return m.CallMsg.To }
func (m callmsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callmsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callmsg) Value() *big.Int { return m.CallMsg.Value }
func (m callmsg) Data() []byte { return m.CallMsg.Data }
func (m callMsg) From() common.Address { return m.CallMsg.From }
func (m callMsg) Nonce() uint64 { return 0 }
func (m callMsg) CheckNonce() bool { return false }
func (m callMsg) To() *common.Address { return m.CallMsg.To }
func (m callMsg) GasPrice() *big.Int { return m.CallMsg.GasPrice }
func (m callMsg) Gas() uint64 { return m.CallMsg.Gas }
func (m callMsg) Value() *big.Int { return m.CallMsg.Value }
func (m callMsg) Data() []byte { return m.CallMsg.Data }
// filterBackend implements filters.Backend to support filtering for logs without
// taking bloom-bits acceleration structures into account.
@@ -722,18 +724,18 @@ type filterBackend struct {
func (fb *filterBackend) ChainDb() ethdb.Database { return fb.db }
func (fb *filterBackend) EventMux() *event.TypeMux { panic("not supported") }
func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumber) (*types.Header, error) {
func (fb *filterBackend) HeaderByNumber(_ context.Context, block rpc.BlockNumber) (*types.Header, error) {
if block == rpc.LatestBlockNumber {
return fb.bc.CurrentHeader(), nil
}
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
}
func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
func (fb *filterBackend) HeaderByHash(_ context.Context, hash common.Hash) (*types.Header, error) {
return fb.bc.GetHeaderByHash(hash), nil
}
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
func (fb *filterBackend) GetReceipts(_ context.Context, hash common.Hash) (types.Receipts, error) {
number := rawdb.ReadHeaderNumber(fb.db, hash)
if number == nil {
return nil, nil
@@ -741,7 +743,7 @@ func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (typ
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
}
func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
func (fb *filterBackend) GetLogs(_ context.Context, hash common.Hash) ([][]*types.Log, error) {
number := rawdb.ReadHeaderNumber(fb.db, hash)
if number == nil {
return nil, nil
@@ -757,7 +759,7 @@ func (fb *filterBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*ty
return logs, nil
}
func (fb *filterBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription {
func (fb *filterBackend) SubscribeNewTxsEvent(_ chan<- core.NewTxsEvent) event.Subscription {
return nullSubscription()
}
@@ -773,13 +775,13 @@ func (fb *filterBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscr
return fb.bc.SubscribeLogsEvent(ch)
}
func (fb *filterBackend) SubscribePendingLogsEvent(ch chan<- []*types.Log) event.Subscription {
func (fb *filterBackend) SubscribePendingLogsEvent(_ 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) {
func (fb *filterBackend) ServiceFilter(_ context.Context, _ *bloombits.MatcherSession) {
panic("not supported")
}

View File

@@ -129,8 +129,8 @@ func TestNewSimulatedBackend(t *testing.T) {
t.Errorf("expected sim blockchain config to equal params.AllEthashProtocolChanges, got %v", sim.config)
}
statedb, _ := sim.blockchain.State()
bal := statedb.GetBalance(testAddr)
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)
}
@@ -143,8 +143,7 @@ func TestSimulatedBackend_AdjustTime(t *testing.T) {
defer sim.Close()
prevTime := sim.pendingBlock.Time()
err := sim.AdjustTime(time.Second)
if err != nil {
if err := sim.AdjustTime(time.Second); err != nil {
t.Error(err)
}
newTime := sim.pendingBlock.Time()
@@ -154,6 +153,44 @@ func TestSimulatedBackend_AdjustTime(t *testing.T) {
}
}
func TestNewSimulatedBackend_AdjustTimeFail(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
sim := simTestBackend(testAddr)
// Create tx and send
tx := types.NewTransaction(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)
}
sim.SendTransaction(context.Background(), signedTx)
// AdjustTime should fail on non-empty block
if err := sim.AdjustTime(time.Second); err == nil {
t.Error("Expected adjust time to error on non-empty block")
}
sim.Commit()
prevTime := sim.pendingBlock.Time()
if err := sim.AdjustTime(time.Minute); err != nil {
t.Error(err)
}
newTime := sim.pendingBlock.Time()
if newTime-prevTime != uint64(time.Minute.Seconds()) {
t.Errorf("adjusted time not equal to a minute. prev: %v, new: %v", prevTime, newTime)
}
// Put a transaction after adjusting time
tx2 := types.NewTransaction(1, testAddr, big.NewInt(1000), params.TxGas, big.NewInt(1), nil)
signedTx2, err := types.SignTx(tx2, types.HomesteadSigner{}, testKey)
if err != nil {
t.Errorf("could not sign tx: %v", err)
}
sim.SendTransaction(context.Background(), signedTx2)
sim.Commit()
newTime = sim.pendingBlock.Time()
if newTime-prevTime >= uint64(time.Minute.Seconds()) {
t.Errorf("time adjusted, but shouldn't be: prev: %v, new: %v", prevTime, newTime)
}
}
func TestSimulatedBackend_BalanceAt(t *testing.T) {
testAddr := crypto.PubkeyToAddress(testKey.PublicKey)
expectedBal := big.NewInt(10000000000)
@@ -484,7 +521,7 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
sim := NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(params.Ether*2 + 2e17)}}, 10000000)
defer sim.Close()
receipant := common.HexToAddress("deadbeef")
recipient := common.HexToAddress("deadbeef")
var cases = []struct {
name string
message ethereum.CallMsg
@@ -493,7 +530,7 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
}{
{"EstimateWithoutPrice", ethereum.CallMsg{
From: addr,
To: &receipant,
To: &recipient,
Gas: 0,
GasPrice: big.NewInt(0),
Value: big.NewInt(1000),
@@ -502,7 +539,7 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
{"EstimateWithPrice", ethereum.CallMsg{
From: addr,
To: &receipant,
To: &recipient,
Gas: 0,
GasPrice: big.NewInt(1000),
Value: big.NewInt(1000),
@@ -511,7 +548,7 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
{"EstimateWithVeryHighPrice", ethereum.CallMsg{
From: addr,
To: &receipant,
To: &recipient,
Gas: 0,
GasPrice: big.NewInt(1e14), // gascost = 2.1ether
Value: big.NewInt(1e17), // the remaining balance for fee is 2.1ether
@@ -520,7 +557,7 @@ func TestSimulatedBackend_EstimateGasWithPrice(t *testing.T) {
{"EstimateWithSuperhighPrice", ethereum.CallMsg{
From: addr,
To: &receipant,
To: &recipient,
Gas: 0,
GasPrice: big.NewInt(2e14), // gascost = 4.2ether
Value: big.NewInt(1000),
@@ -1049,12 +1086,12 @@ func TestSimulatedBackend_CallContractRevert(t *testing.T) {
t.Errorf("result from %v was not nil: %v", key, res)
}
if val != nil {
rerr, ok := err.(*revertError)
rErr, ok := err.(*revertError)
if !ok {
t.Errorf("expect revert error")
}
if rerr.Error() != "execution reverted: "+val.(string) {
t.Errorf("error was malformed: got %v want %v", rerr.Error(), val)
if rErr.Error() != "execution reverted: "+val.(string) {
t.Errorf("error was malformed: got %v want %v", rErr.Error(), val)
}
} else {
// revert(0x0,0x0)

View File

@@ -386,7 +386,7 @@ func isDynamicType(t Type) bool {
func getTypeSize(t Type) int {
if t.T == ArrayTy && !isDynamicType(*t.Elem) {
// Recursively calculate type size if it is a nested array
if t.Elem.T == ArrayTy {
if t.Elem.T == ArrayTy || t.Elem.T == TupleTy {
return t.Size * getTypeSize(*t.Elem)
}
return t.Size * 32

View File

@@ -330,3 +330,39 @@ func TestInternalType(t *testing.T) {
t.Errorf("type %q: parsed type mismatch:\nGOT %s\nWANT %s ", blob, spew.Sdump(typeWithoutStringer(typ)), spew.Sdump(typeWithoutStringer(kind)))
}
}
func TestGetTypeSize(t *testing.T) {
var testCases = []struct {
typ string
components []ArgumentMarshaling
typSize int
}{
// simple array
{"uint256[2]", nil, 32 * 2},
{"address[3]", nil, 32 * 3},
{"bytes32[4]", nil, 32 * 4},
// array array
{"uint256[2][3][4]", nil, 32 * (2 * 3 * 4)},
// array tuple
{"tuple[2]", []ArgumentMarshaling{{Name: "x", Type: "bytes32"}, {Name: "y", Type: "bytes32"}}, (32 * 2) * 2},
// simple tuple
{"tuple", []ArgumentMarshaling{{Name: "x", Type: "uint256"}, {Name: "y", Type: "uint256"}}, 32 * 2},
// tuple array
{"tuple", []ArgumentMarshaling{{Name: "x", Type: "bytes32[2]"}}, 32 * 2},
// tuple tuple
{"tuple", []ArgumentMarshaling{{Name: "x", Type: "tuple", Components: []ArgumentMarshaling{{Name: "x", Type: "bytes32"}}}}, 32},
{"tuple", []ArgumentMarshaling{{Name: "x", Type: "tuple", Components: []ArgumentMarshaling{{Name: "x", Type: "bytes32[2]"}, {Name: "y", Type: "uint256"}}}}, 32 * (2 + 1)},
}
for i, data := range testCases {
typ, err := NewType(data.typ, "", data.components)
if err != nil {
t.Errorf("type %q: failed to parse type string: %v", data.typ, err)
}
result := getTypeSize(typ)
if result != data.typSize {
t.Errorf("case %d type %q: get type size error: actual: %d expected: %d", i, data.typ, result, data.typSize)
}
}
}

View File

@@ -368,18 +368,22 @@ func (w *wallet) selfDerive() {
w.log.Warn("USB wallet nonce retrieval failed", "err", err)
break
}
// If the next account is empty, stop self-derivation, but add for the last base path
// We've just self-derived a new account, start tracking it locally
// unless the account was empty.
path := make(accounts.DerivationPath, len(nextPaths[i]))
copy(path[:], nextPaths[i][:])
if balance.Sign() == 0 && nonce == 0 {
empty = true
// If it indeed was empty, make a log output for it anyway. In the case
// of legacy-ledger, the first account on the legacy-path will
// be shown to the user, even if we don't actively track it
if i < len(nextAddrs)-1 {
w.log.Info("Skipping trakcking first account on legacy path, use personal.deriveAccount(<url>,<path>, false) to track",
"path", path, "address", nextAddrs[i])
break
}
}
// We've just self-derived a new account, start tracking it locally
path := make(accounts.DerivationPath, len(nextPaths[i]))
copy(path[:], nextPaths[i][:])
paths = append(paths, path)
account := accounts.Account{
Address: nextAddrs[i],
URL: accounts.URL{Scheme: w.url.Scheme, Path: fmt.Sprintf("%s/%s", w.url.Path, path)},

View File

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

View File

@@ -1,6 +1,6 @@
# This file contains sha256 checksums of optional build dependencies.
98de84e69726a66da7b4e58eac41b99cbe274d7e8906eeb8a5b7eb0aadee7f7c go1.14.2.src.tar.gz
69438f7ed4f532154ffaf878f3dfd83747e7a00b70b3556eddabf7aaee28ac3a go1.15.src.tar.gz
d998a84eea42f2271aca792a7b027ca5c1edfcba229e8e5a844c9ac3f336df35 golangci-lint-1.27.0-linux-armv7.tar.gz
bf781f05b0d393b4bf0a327d9e62926949a4f14d7774d950c4e009fc766ed1d4 golangci-lint.exe-1.27.0-windows-amd64.zip

View File

@@ -79,7 +79,6 @@ var (
executablePath("geth"),
executablePath("puppeth"),
executablePath("rlpdump"),
executablePath("wnode"),
executablePath("clef"),
}
@@ -109,10 +108,6 @@ var (
BinaryName: "rlpdump",
Description: "Developer utility tool that prints RLP structures.",
},
{
BinaryName: "wnode",
Description: "Ethereum Whisper diagnostic tool",
},
{
BinaryName: "clef",
Description: "Ethereum account management tool.",
@@ -139,13 +134,14 @@ var (
// Note: zesty is unsupported because it was officially deprecated on Launchpad.
// Note: artful is unsupported because it was officially deprecated on Launchpad.
// Note: cosmic is unsupported because it was officially deprecated on Launchpad.
// Note: disco is unsupported because it was officially deprecated on Launchpad.
debDistroGoBoots = map[string]string{
"trusty": "golang-1.11",
"xenial": "golang-go",
"bionic": "golang-go",
"disco": "golang-go",
"eoan": "golang-go",
"focal": "golang-go",
"groovy": "golang-go",
}
debGoBootPaths = map[string]string{
@@ -215,9 +211,9 @@ func doInstall(cmdline []string) {
var minor int
fmt.Sscanf(strings.TrimPrefix(runtime.Version(), "go1."), "%d", &minor)
if minor < 11 {
if minor < 13 {
log.Println("You have Go version", runtime.Version())
log.Println("go-ethereum requires at least Go version 1.11 and cannot")
log.Println("go-ethereum requires at least Go version 1.13 and cannot")
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
os.Exit(1)
}
@@ -233,6 +229,7 @@ func doInstall(cmdline []string) {
if runtime.GOARCH == "arm64" {
goinstall.Args = append(goinstall.Args, "-p", "1")
}
goinstall.Args = append(goinstall.Args, "-trimpath")
goinstall.Args = append(goinstall.Args, "-v")
goinstall.Args = append(goinstall.Args, packages...)
build.MustRun(goinstall)
@@ -241,6 +238,7 @@ func doInstall(cmdline []string) {
// Seems we are cross compiling, work around forbidden GOBIN
goinstall := goToolArch(*arch, *cc, "install", buildFlags(env)...)
goinstall.Args = append(goinstall.Args, "-trimpath")
goinstall.Args = append(goinstall.Args, "-v")
goinstall.Args = append(goinstall.Args, []string{"-buildmode", "archive"}...)
goinstall.Args = append(goinstall.Args, packages...)
@@ -884,11 +882,12 @@ func gomobileTool(subcmd string, args ...string) *exec.Cmd {
"PATH=" + GOBIN + string(os.PathListSeparator) + os.Getenv("PATH"),
}
for _, e := range os.Environ() {
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "PATH=") {
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "PATH=") || strings.HasPrefix(e, "GOBIN=") {
continue
}
cmd.Env = append(cmd.Env, e)
}
cmd.Env = append(cmd.Env, "GOBIN="+GOBIN)
return cmd
}
@@ -957,7 +956,7 @@ func doXCodeFramework(cmdline []string) {
if *local {
// If we're building locally, use the build folder and stop afterwards
bind.Dir, _ = filepath.Abs(GOBIN)
bind.Dir = GOBIN
build.MustRun(bind)
return
}

View File

@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common/compiler"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/log"
"gopkg.in/urfave/cli.v1"
)
@@ -100,7 +101,7 @@ var (
)
func init() {
app = utils.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
app = flags.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
app.Flags = []cli.Flag{
abiFlag,
binFlag,
@@ -117,7 +118,7 @@ func init() {
aliasFlag,
}
app.Action = utils.MigrateFlags(abigen)
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
}
func abigen(c *cli.Context) error {

View File

@@ -22,8 +22,8 @@ import (
"fmt"
"os"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/log"
"gopkg.in/urfave/cli.v1"
)
@@ -37,7 +37,7 @@ var (
var app *cli.App
func init() {
app = utils.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
app = flags.NewApp(gitCommit, gitDate, "ethereum checkpoint helper tool")
app.Commands = []cli.Command{
commandStatus,
commandDeploy,
@@ -48,7 +48,7 @@ func init() {
oracleFlag,
nodeURLFlag,
}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
}
// Commonly used command line flags.

View File

@@ -33,12 +33,12 @@ GLOBAL OPTIONS:
--lightkdf Reduce key-derivation RAM & CPU usage at some expense of KDF strength
--nousb Disables monitoring for and managing USB hardware wallets
--pcscdpath value Path to the smartcard daemon (pcscd) socket file (default: "/run/pcscd/pcscd.comm")
--rpcaddr value HTTP-RPC server listening interface (default: "localhost")
--rpcvhosts value Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost")
--http.addr value HTTP-RPC server listening interface (default: "localhost")
--http.vhosts value Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (default: "localhost")
--ipcdisable Disable the IPC-RPC server
--ipcpath Filename for IPC socket/pipe within the datadir (explicit paths escape it)
--rpc Enable the HTTP-RPC server
--rpcport value HTTP-RPC server listening port (default: 8550)
--http Enable the HTTP-RPC server
--http.port value HTTP-RPC server listening port (default: 8550)
--signersecret value A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash
--4bytedb-custom value File used for writing new 4byte-identifiers submitted via API (default: "./4byte-custom.json")
--auditlog value File used to emit audit logs. Set to "" to disable (default: "audit.log")
@@ -113,7 +113,7 @@ Some snags and todos
### External API
Clef listens to HTTP requests on `rpcaddr`:`rpcport` (or to IPC on `ipcpath`), with the same JSON-RPC standard as Geth. The messages are expected to be [JSON-RPC 2.0 standard](https://www.jsonrpc.org/specification).
Clef listens to HTTP requests on `http.addr`:`http.port` (or to IPC on `ipcpath`), with the same JSON-RPC standard as Geth. The messages are expected to be [JSON-RPC 2.0 standard](https://www.jsonrpc.org/specification).
Some of these calls can require user interaction. Clients must be aware that responses may be delayed significantly or may never be received if a user decides to ignore the confirmation request.

View File

@@ -94,7 +94,7 @@ with minimal requirements.
On the `client` qube, we need to create a listener which will receive the request from the Dapp, and proxy it.
[qubes-client.py](qubes/client/qubes-client.py):
[qubes-client.py](qubes/qubes-client.py):
```python

View File

@@ -32,6 +32,7 @@ import (
"os/user"
"path/filepath"
"runtime"
"sort"
"strings"
"time"
@@ -43,6 +44,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
@@ -52,6 +54,7 @@ import (
"github.com/ethereum/go-ethereum/signer/fourbyte"
"github.com/ethereum/go-ethereum/signer/rules"
"github.com/ethereum/go-ethereum/signer/storage"
colorable "github.com/mattn/go-colorable"
"github.com/mattn/go-isatty"
"gopkg.in/urfave/cli.v1"
@@ -101,10 +104,15 @@ var (
Usage: "Chain id to use for signing (1=mainnet, 3=Ropsten, 4=Rinkeby, 5=Goerli)",
}
rpcPortFlag = cli.IntFlag{
Name: "rpcport",
Name: "http.port",
Usage: "HTTP-RPC server listening port",
Value: node.DefaultHTTPPort + 5,
}
legacyRPCPortFlag = cli.IntFlag{
Name: "rpcport",
Usage: "HTTP-RPC server listening port (Deprecated, please use --http.port).",
Value: node.DefaultHTTPPort + 5,
}
signerSecretFlag = cli.StringFlag{
Name: "signersecret",
Usage: "A file containing the (encrypted) master seed to encrypt Clef data, e.g. keystore credentials and ruleset hash",
@@ -215,6 +223,42 @@ The gendoc generates example structures of the json-rpc communication types.
`}
)
// AppHelpFlagGroups is the application flags, grouped by functionality.
var AppHelpFlagGroups = []flags.FlagGroup{
{
Name: "FLAGS",
Flags: []cli.Flag{
logLevelFlag,
keystoreFlag,
configdirFlag,
chainIdFlag,
utils.LightKDFFlag,
utils.NoUSBFlag,
utils.SmartCardDaemonPathFlag,
utils.HTTPListenAddrFlag,
utils.HTTPVirtualHostsFlag,
utils.IPCDisabledFlag,
utils.IPCPathFlag,
utils.HTTPEnabledFlag,
rpcPortFlag,
signerSecretFlag,
customDBFlag,
auditLogFlag,
ruleFlag,
stdiouiFlag,
testFlag,
advancedMode,
acceptFlag,
},
},
{
Name: "ALIASED (deprecated)",
Flags: []cli.Flag{
legacyRPCPortFlag,
},
},
}
func init() {
app.Name = "Clef"
app.Usage = "Manage Ethereum account operations"
@@ -240,6 +284,7 @@ func init() {
testFlag,
advancedMode,
acceptFlag,
legacyRPCPortFlag,
}
app.Action = signer
app.Commands = []cli.Command{initCommand,
@@ -248,7 +293,41 @@ func init() {
delCredentialCommand,
newAccountCommand,
gendocCommand}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
cli.CommandHelpTemplate = flags.CommandHelpTemplate
// Override the default app help template
cli.AppHelpTemplate = flags.ClefAppHelpTemplate
// Override the default app help printer, but only for the global app help
originalHelpPrinter := cli.HelpPrinter
cli.HelpPrinter = func(w io.Writer, tmpl string, data interface{}) {
if tmpl == flags.ClefAppHelpTemplate {
// Render out custom usage screen
originalHelpPrinter(w, tmpl, flags.HelpData{App: data, FlagGroups: AppHelpFlagGroups})
} else if tmpl == flags.CommandHelpTemplate {
// Iterate over all command specific flags and categorize them
categorized := make(map[string][]cli.Flag)
for _, flag := range data.(cli.Command).Flags {
if _, ok := categorized[flag.String()]; !ok {
categorized[flags.FlagCategory(flag, AppHelpFlagGroups)] = append(categorized[flags.FlagCategory(flag, AppHelpFlagGroups)], flag)
}
}
// sort to get a stable ordering
sorted := make([]flags.FlagGroup, 0, len(categorized))
for cat, flgs := range categorized {
sorted = append(sorted, flags.FlagGroup{Name: cat, Flags: flgs})
}
sort.Sort(flags.ByCategory(sorted))
// add sorted array to data and render with default printer
originalHelpPrinter(w, tmpl, map[string]interface{}{
"cmd": data,
"categorizedFlags": sorted,
})
} else {
originalHelpPrinter(w, tmpl, data)
}
}
}
func main() {
@@ -597,8 +676,17 @@ func signer(c *cli.Context) error {
}
handler := node.NewHTTPHandlerStack(srv, cors, vhosts)
// set port
port := c.Int(rpcPortFlag.Name)
if c.GlobalIsSet(legacyRPCPortFlag.Name) {
if !c.GlobalIsSet(rpcPortFlag.Name) {
port = c.Int(legacyRPCPortFlag.Name)
}
log.Warn("The flag --rpcport is deprecated and will be removed in the future, please use --http.port")
}
// start http server
httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.HTTPListenAddrFlag.Name), c.Int(rpcPortFlag.Name))
httpEndpoint := fmt.Sprintf("%s:%d", c.GlobalString(utils.HTTPListenAddrFlag.Name), port)
httpServer, addr, err := node.StartHTTPEndpoint(httpEndpoint, rpc.DefaultHTTPTimeouts, handler)
if err != nil {
utils.Fatalf("Could not start RPC api: %v", err)

View File

@@ -21,6 +21,7 @@ import (
"encoding/base64"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"net"
"os"
@@ -69,22 +70,30 @@ func enrdump(ctx *cli.Context) error {
if err != nil {
return fmt.Errorf("INVALID: %v", err)
}
fmt.Print(dumpRecord(r))
dumpRecord(os.Stdout, r)
return nil
}
// dumpRecord creates a human-readable description of the given node record.
func dumpRecord(r *enr.Record) string {
out := new(bytes.Buffer)
if n, err := enode.New(enode.ValidSchemes, r); err != nil {
func dumpRecord(out io.Writer, r *enr.Record) {
n, err := enode.New(enode.ValidSchemes, r)
if err != nil {
fmt.Fprintf(out, "INVALID: %v\n", err)
} else {
fmt.Fprintf(out, "Node ID: %v\n", n.ID())
dumpNodeURL(out, n)
}
kv := r.AppendElements(nil)[1:]
fmt.Fprintf(out, "Record has sequence number %d and %d key/value pairs.\n", r.Seq(), len(kv)/2)
fmt.Fprint(out, dumpRecordKV(kv, 2))
return out.String()
}
func dumpNodeURL(out io.Writer, n *enode.Node) {
var key enode.Secp256k1
if n.Load(&key) != nil {
return // no secp256k1 public key
}
fmt.Fprintf(out, "URLv4: %s\n", n.URLv4())
}
func dumpRecordKV(kv []interface{}, indent int) string {

View File

@@ -20,7 +20,7 @@ import (
"fmt"
"os"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/flags"
"gopkg.in/urfave/cli.v1"
)
@@ -35,7 +35,7 @@ var gitDate = ""
var app *cli.App
func init() {
app = utils.NewApp(gitCommit, gitDate, "an Ethereum key manager")
app = flags.NewApp(gitCommit, gitDate, "an Ethereum key manager")
app.Commands = []cli.Command{
commandGenerate,
commandInspect,
@@ -43,7 +43,7 @@ func init() {
commandSignMessage,
commandVerifyMessage,
}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
}
// Commonly used command line flags.

View File

@@ -29,6 +29,8 @@ Command line params that has to be supported are
--trace Output full trace logs to files <txhash>.jsonl
--trace.nomemory Disable full memory dump in traces
--trace.nostack Disable stack output in traces
--trace.noreturndata Disable return data output in traces
--output.basedir value Specifies where output files are placed. Will be created if it does not exist. (default: ".")
--output.alloc alloc Determines where to put the alloc of the post-state.
`stdout` - into the stdout output
`stderr` - into the stderr output
@@ -232,13 +234,13 @@ Example where blockhashes are provided:
./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace
```
```
cat trace-0.jsonl | grep BLOCKHASH -C2
cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2
```
```
{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"depth":1,"refund":0,"opName":"PUSH1","error":""}
{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"depth":1,"refund":0,"opName":"STOP","error":""}
{"output":"","gasUsed":"0x17","time":155861}
{"pc":0,"op":96,"gas":"0x5f58ef8","gasCost":"0x3","memory":"0x","memSize":0,"stack":[],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"PUSH1","error":""}
{"pc":2,"op":64,"gas":"0x5f58ef5","gasCost":"0x14","memory":"0x","memSize":0,"stack":["0x1"],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"BLOCKHASH","error":""}
{"pc":3,"op":0,"gas":"0x5f58ee1","gasCost":"0x0","memory":"0x","memSize":0,"stack":["0xdac58aa524e50956d0c0bae7f3f8bb9d35381365d07804dd5b48a5a297c06af4"],"returnStack":[],"returnData":null,"depth":1,"refund":0,"opName":"STOP","error":""}
{"output":"","gasUsed":"0x17","time":112885}
```
In this example, the caller has not provided the required blockhash:
@@ -254,9 +256,9 @@ Error code: 4
Another thing that can be done, is to chain invocations:
```
./evm t8n --input.alloc=./testdata/1/alloc.json --input.txs=./testdata/1/txs.json --input.env=./testdata/1/env.json --output.alloc=stdout | ./evm t8n --input.alloc=stdin --input.env=./testdata/1/env.json --input.txs=./testdata/1/txs.json
INFO [06-29|11:52:04.934] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
INFO [06-29|11:52:04.936] rejected tx index=0 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
INFO [06-29|11:52:04.936] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
INFO [08-03|15:25:15.168] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
INFO [08-03|15:25:15.169] rejected tx index=0 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
INFO [08-03|15:25:15.169] rejected tx index=1 hash="0557ba…18d673" from=0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192 error="nonce too low"
```
What happened here, is that we first applied two identical transactions, so the second one was rejected.

View File

@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/crypto/sha3"
)
@@ -81,7 +82,7 @@ type stEnvMarshaling struct {
// Apply applies a set of transactions to a pre-state
func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
txs types.Transactions, miningReward int64,
getTracerFn func(txIndex int) (tracer vm.Tracer, err error)) (*state.StateDB, *ExecutionResult, error) {
getTracerFn func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error)) (*state.StateDB, *ExecutionResult, error) {
// Capture errors for BLOCKHASH operation, if we haven't been supplied the
// required blockhashes
@@ -135,7 +136,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
rejectedTxs = append(rejectedTxs, i)
continue
}
tracer, err := getTracerFn(txIndex)
tracer, err := getTracerFn(txIndex, tx.Hash())
if err != nil {
return nil, nil, err
}
@@ -220,8 +221,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
}
execRs := &ExecutionResult{
StateRoot: root,
TxRoot: types.DeriveSha(includedTxs),
ReceiptRoot: types.DeriveSha(receipts),
TxRoot: types.DeriveSha(includedTxs, new(trie.Trie)),
ReceiptRoot: types.DeriveSha(receipts, new(trie.Trie)),
Bloom: types.CreateBloom(receipts),
LogsHash: rlpHash(statedb.Logs()),
Receipts: receipts,

View File

@@ -38,6 +38,15 @@ var (
Name: "trace.nostack",
Usage: "Disable stack output in traces",
}
TraceDisableReturnDataFlag = cli.BoolFlag{
Name: "trace.noreturndata",
Usage: "Disable return data output in traces",
}
OutputBasedir = cli.StringFlag{
Name: "output.basedir",
Usage: "Specifies where output files are placed. Will be created if it does not exist.",
Value: "",
}
OutputAllocFlag = cli.StringFlag{
Name: "output.alloc",
Usage: "Determines where to put the `alloc` of the post-state.\n" +

View File

@@ -22,6 +22,7 @@ import (
"io/ioutil"
"math/big"
"os"
"path"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
@@ -75,17 +76,29 @@ func Main(ctx *cli.Context) error {
log.Root().SetHandler(glogger)
var (
err error
tracer vm.Tracer
err error
tracer vm.Tracer
baseDir = ""
)
var getTracer func(txIndex int) (vm.Tracer, error)
var getTracer func(txIndex int, txHash common.Hash) (vm.Tracer, error)
// If user specified a basedir, make sure it exists
if ctx.IsSet(OutputBasedir.Name) {
if base := ctx.String(OutputBasedir.Name); len(base) > 0 {
err := os.MkdirAll(base, 0755) // //rw-r--r--
if err != nil {
return NewError(ErrorIO, fmt.Errorf("failed creating output basedir: %v", err))
}
baseDir = base
}
}
if ctx.Bool(TraceFlag.Name) {
// Configure the EVM logger
logConfig := &vm.LogConfig{
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
Debug: true,
DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
Debug: true,
}
var prevFile *os.File
// This one closes the last file
@@ -94,11 +107,11 @@ func Main(ctx *cli.Context) error {
prevFile.Close()
}
}()
getTracer = func(txIndex int) (vm.Tracer, error) {
getTracer = func(txIndex int, txHash common.Hash) (vm.Tracer, error) {
if prevFile != nil {
prevFile.Close()
}
traceFile, err := os.Create(fmt.Sprintf("trace-%d.jsonl", txIndex))
traceFile, err := os.Create(path.Join(baseDir, fmt.Sprintf("trace-%d-%v.jsonl", txIndex, txHash.String())))
if err != nil {
return nil, NewError(ErrorIO, fmt.Errorf("failed creating trace-file: %v", err))
}
@@ -106,7 +119,7 @@ func Main(ctx *cli.Context) error {
return vm.NewJSONLogger(logConfig, traceFile), nil
}
} else {
getTracer = func(txIndex int) (tracer vm.Tracer, err error) {
getTracer = func(txIndex int, txHash common.Hash) (tracer vm.Tracer, err error) {
return nil, nil
}
}
@@ -196,7 +209,7 @@ func Main(ctx *cli.Context) error {
//postAlloc := state.DumpGenesisFormat(false, false, false)
collector := make(Alloc)
state.DumpToCollector(collector, false, false, false, nil, -1)
return dispatchOutput(ctx, result, collector)
return dispatchOutput(ctx, baseDir, result, collector)
}
@@ -223,12 +236,12 @@ func (g Alloc) OnAccount(addr common.Address, dumpAccount state.DumpAccount) {
}
// saveFile marshalls the object to the given file
func saveFile(filename string, data interface{}) error {
func saveFile(baseDir, filename string, data interface{}) error {
b, err := json.MarshalIndent(data, "", " ")
if err != nil {
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
}
if err = ioutil.WriteFile(filename, b, 0644); err != nil {
if err = ioutil.WriteFile(path.Join(baseDir, filename), b, 0644); err != nil {
return NewError(ErrorIO, fmt.Errorf("failed writing output: %v", err))
}
return nil
@@ -236,26 +249,26 @@ func saveFile(filename string, data interface{}) error {
// dispatchOutput writes the output data to either stderr or stdout, or to the specified
// files
func dispatchOutput(ctx *cli.Context, result *ExecutionResult, alloc Alloc) error {
func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, alloc Alloc) error {
stdOutObject := make(map[string]interface{})
stdErrObject := make(map[string]interface{})
dispatch := func(fName, name string, obj interface{}) error {
dispatch := func(baseDir, fName, name string, obj interface{}) error {
switch fName {
case "stdout":
stdOutObject[name] = obj
case "stderr":
stdErrObject[name] = obj
default: // save to file
if err := saveFile(fName, obj); err != nil {
if err := saveFile(baseDir, fName, obj); err != nil {
return err
}
}
return nil
}
if err := dispatch(ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
if err := dispatch(baseDir, ctx.String(OutputAllocFlag.Name), "alloc", alloc); err != nil {
return err
}
if err := dispatch(ctx.String(OutputResultFlag.Name), "result", result); err != nil {
if err := dispatch(baseDir, ctx.String(OutputResultFlag.Name), "result", result); err != nil {
return err
}
if len(stdOutObject) > 0 {

View File

@@ -24,6 +24,7 @@ import (
"github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/flags"
"gopkg.in/urfave/cli.v1"
)
@@ -31,7 +32,7 @@ var gitCommit = "" // Git SHA1 commit hash of the release (set via linker flags)
var gitDate = ""
var (
app = utils.NewApp(gitCommit, gitDate, "the evm command line interface")
app = flags.NewApp(gitCommit, gitDate, "the evm command line interface")
DebugFlag = cli.BoolFlag{
Name: "debug",
@@ -120,6 +121,14 @@ var (
Name: "nostack",
Usage: "disable stack output",
}
DisableStorageFlag = cli.BoolFlag{
Name: "nostorage",
Usage: "disable storage output",
}
DisableReturnDataFlag = cli.BoolFlag{
Name: "noreturndata",
Usage: "disable return data output",
}
EVMInterpreterFlag = cli.StringFlag{
Name: "vm.evm",
Usage: "External EVM configuration (default = built-in interpreter)",
@@ -136,6 +145,8 @@ var stateTransitionCommand = cli.Command{
t8ntool.TraceFlag,
t8ntool.TraceDisableMemoryFlag,
t8ntool.TraceDisableStackFlag,
t8ntool.TraceDisableReturnDataFlag,
t8ntool.OutputBasedir,
t8ntool.OutputAllocFlag,
t8ntool.OutputResultFlag,
t8ntool.InputAllocFlag,
@@ -171,6 +182,8 @@ func init() {
ReceiverFlag,
DisableMemoryFlag,
DisableStackFlag,
DisableStorageFlag,
DisableReturnDataFlag,
EVMInterpreterFlag,
}
app.Commands = []cli.Command{
@@ -180,7 +193,7 @@ func init() {
stateTestCommand,
stateTransitionCommand,
}
cli.CommandHelpTemplate = utils.OriginCommandHelpTemplate
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
}
func main() {

View File

@@ -108,9 +108,11 @@ func runCmd(ctx *cli.Context) error {
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
log.Root().SetHandler(glogger)
logconfig := &vm.LogConfig{
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
Debug: ctx.GlobalBool(DebugFlag.Name),
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
Debug: ctx.GlobalBool(DebugFlag.Name),
}
var (

View File

@@ -59,8 +59,10 @@ func stateTestCmd(ctx *cli.Context) error {
// Configure the EVM logger
config := &vm.LogConfig{
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
}
var (
tracer vm.Tracer

View File

@@ -155,10 +155,10 @@ echo "Example where blockhashes are provided: "
cmd="./evm t8n --input.alloc=./testdata/3/alloc.json --input.txs=./testdata/3/txs.json --input.env=./testdata/3/env.json --trace"
tick && echo $cmd && tick
$cmd 2>&1 >/dev/null
cmd="cat trace-0.jsonl | grep BLOCKHASH -C2"
cmd="cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2"
tick && echo $cmd && tick
echo "$ticks"
cat trace-0.jsonl | grep BLOCKHASH -C2
cat trace-0-0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81.jsonl | grep BLOCKHASH -C2
echo "$ticks"
echo ""

View File

@@ -235,23 +235,20 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
if err != nil {
return nil, err
}
// Assemble the Ethereum light client protocol
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
cfg := eth.DefaultConfig
cfg.SyncMode = downloader.LightSync
cfg.NetworkId = network
cfg.Genesis = genesis
return les.New(ctx, &cfg)
}); err != nil {
return nil, err
cfg := eth.DefaultConfig
cfg.SyncMode = downloader.LightSync
cfg.NetworkId = network
cfg.Genesis = genesis
lesBackend, err := les.New(stack, &cfg)
if err != nil {
return nil, fmt.Errorf("Failed to register the Ethereum service: %w", err)
}
// Assemble the ethstats monitoring and reporting service'
if stats != "" {
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
var serv *les.LightEthereum
ctx.Service(&serv)
return ethstats.New(stats, nil, serv)
}); err != nil {
if err := ethstats.New(stack, lesBackend.ApiBackend, lesBackend.Engine(), stats); err != nil {
return nil, err
}
}
@@ -268,7 +265,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*discv5.Node, network u
// Attach to the client and retrieve and interesting metadatas
api, err := stack.Attach()
if err != nil {
stack.Stop()
stack.Close()
return nil, err
}
client := ethclient.NewClient(api)

View File

@@ -49,7 +49,7 @@
<div class="row">
<div class="col-lg-8 col-lg-offset-2">
<div class="input-group">
<input id="url" name="url" type="text" class="form-control" placeholder="Social network URL containing your Ethereum address...">
<input id="url" name="url" type="text" class="form-control" placeholder="Social network URL containing your Ethereum address..."/>
<span class="input-group-btn">
<button class="btn btn-default dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Give me Ether <i class="fa fa-caret-down" aria-hidden="true"></i></button>
<ul class="dropdown-menu dropdown-menu-right">{{range $idx, $amount := .Amounts}}

View File

@@ -239,8 +239,8 @@ func initGenesis(ctx *cli.Context) error {
if err := json.NewDecoder(file).Decode(genesis); err != nil {
utils.Fatalf("invalid genesis file: %v", err)
}
// Open an initialise both full and light databases
stack := makeFullNode(ctx)
// Open and initialise both full and light databases
stack, _ := makeConfigNode(ctx)
defer stack.Close()
for _, name := range []string{"chaindata", "lightchaindata"} {
@@ -277,7 +277,8 @@ func importChain(ctx *cli.Context) error {
utils.SetupMetrics(ctx)
// Start system runtime metrics collection
go metrics.CollectProcessMetrics(3 * time.Second)
stack := makeFullNode(ctx)
stack, _ := makeConfigNode(ctx)
defer stack.Close()
chain, db := utils.MakeChain(ctx, stack, false)
@@ -371,7 +372,8 @@ func exportChain(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
stack := makeFullNode(ctx)
stack, _ := makeConfigNode(ctx)
defer stack.Close()
chain, _ := utils.MakeChain(ctx, stack, true)
@@ -406,7 +408,8 @@ func importPreimages(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
stack := makeFullNode(ctx)
stack, _ := makeConfigNode(ctx)
defer stack.Close()
db := utils.MakeChainDatabase(ctx, stack)
@@ -424,7 +427,8 @@ func exportPreimages(ctx *cli.Context) error {
if len(ctx.Args()) < 1 {
utils.Fatalf("This command requires an argument.")
}
stack := makeFullNode(ctx)
stack, _ := makeConfigNode(ctx)
defer stack.Close()
db := utils.MakeChainDatabase(ctx, stack)
@@ -446,7 +450,7 @@ func copyDb(ctx *cli.Context) error {
utils.Fatalf("Source ancient chain directory path argument missing")
}
// Initialize a new chain for the running node to sync into
stack := makeFullNode(ctx)
stack, _ := makeConfigNode(ctx)
defer stack.Close()
chain, chainDb := utils.MakeChain(ctx, stack, false)
@@ -554,7 +558,7 @@ func confirmAndRemoveDB(database string, kind string) {
}
func dump(ctx *cli.Context) error {
stack := makeFullNode(ctx)
stack, _ := makeConfigNode(ctx)
defer stack.Close()
chain, chainDb := utils.MakeChain(ctx, stack, true)

View File

@@ -28,9 +28,10 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"github.com/naoina/toml"
)
@@ -74,7 +75,6 @@ type ethstatsConfig struct {
type gethConfig struct {
Eth eth.Config
Shh whisper.Config
Node node.Config
Ethstats ethstatsConfig
}
@@ -104,11 +104,11 @@ func defaultNodeConfig() node.Config {
return cfg
}
// makeConfigNode loads geth configuration and creates a blank node instance.
func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
// Load defaults.
cfg := gethConfig{
Eth: eth.DefaultConfig,
Shh: whisper.DefaultConfig,
Node: defaultNodeConfig(),
}
@@ -129,49 +129,36 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
}
utils.SetShhConfig(ctx, stack, &cfg.Shh)
utils.SetShhConfig(ctx, stack)
return stack, cfg
}
// enableWhisper returns true in case one of the whisper flags is set.
func enableWhisper(ctx *cli.Context) bool {
func checkWhisper(ctx *cli.Context) {
for _, flag := range whisperFlags {
if ctx.GlobalIsSet(flag.GetName()) {
return true
log.Warn("deprecated whisper flag detected. Whisper has been moved to github.com/ethereum/whisper")
}
}
return false
}
func makeFullNode(ctx *cli.Context) *node.Node {
// makeFullNode loads geth configuration and creates the Ethereum backend.
func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
stack, cfg := makeConfigNode(ctx)
utils.RegisterEthService(stack, &cfg.Eth)
// 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)
if shhEnabled || shhAutoEnabled {
if ctx.GlobalIsSet(utils.WhisperMaxMessageSizeFlag.Name) {
cfg.Shh.MaxMessageSize = uint32(ctx.Int(utils.WhisperMaxMessageSizeFlag.Name))
}
if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {
cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)
}
if ctx.GlobalIsSet(utils.WhisperRestrictConnectionBetweenLightClientsFlag.Name) {
cfg.Shh.RestrictConnectionBetweenLightClients = true
}
utils.RegisterShhService(stack, &cfg.Shh)
}
backend := utils.RegisterEthService(stack, &cfg.Eth)
checkWhisper(ctx)
// Configure GraphQL if requested
if ctx.GlobalIsSet(utils.GraphQLEnabledFlag.Name) {
utils.RegisterGraphQLService(stack, cfg.Node.GraphQLEndpoint(), cfg.Node.GraphQLCors, cfg.Node.GraphQLVirtualHosts, cfg.Node.HTTPTimeouts)
utils.RegisterGraphQLService(stack, backend, cfg.Node)
}
// Add the Ethereum Stats daemon if requested.
if cfg.Ethstats.URL != "" {
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
utils.RegisterEthStatsService(stack, backend, cfg.Ethstats.URL)
}
return stack
return stack, backend
}
// dumpConfig is the dumpconfig command.

View File

@@ -78,12 +78,12 @@ JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Cons
func localConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags
prepare(ctx)
node := makeFullNode(ctx)
startNode(ctx, node)
defer node.Close()
stack, backend := makeFullNode(ctx)
startNode(ctx, stack, backend)
defer stack.Close()
// Attach to the newly started node and start the JavaScript console
client, err := node.Attach()
client, err := stack.Attach()
if err != nil {
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
}
@@ -190,12 +190,12 @@ func dialRPC(endpoint string) (*rpc.Client, error) {
// everything down.
func ephemeralConsole(ctx *cli.Context) error {
// Create and start the node based on the CLI flags
node := makeFullNode(ctx)
startNode(ctx, node)
defer node.Close()
stack, backend := makeFullNode(ctx)
startNode(ctx, stack, backend)
defer stack.Close()
// Attach to the newly started node and start the JavaScript console
client, err := node.Attach()
client, err := stack.Attach()
if err != nil {
utils.Fatalf("Failed to attach to the inproc geth: %v", err)
}

View File

@@ -31,7 +31,7 @@ import (
)
const (
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 shh:1.0 txpool:1.0 web3:1.0"
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0"
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
)
@@ -43,7 +43,7 @@ func TestConsoleWelcome(t *testing.T) {
// Start a geth console, make sure it's cleaned up and terminate the console
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--shh",
"--etherbase", coinbase,
"console")
// Gather all the infos the welcome message needs to contain
@@ -83,11 +83,9 @@ func TestIPCAttachWelcome(t *testing.T) {
defer os.RemoveAll(ws)
ipc = filepath.Join(ws, "geth.ipc")
}
// Note: we need --shh because testAttachWelcome checks for default
// list of ipc modules and shh is included there.
geth := runGeth(t,
"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none",
"--etherbase", coinbase, "--shh", "--ipcpath", ipc)
"--etherbase", coinbase, "--ipcpath", ipc)
defer func() {
geth.Interrupt()

View File

@@ -119,8 +119,7 @@ func testDAOForkBlockNewChain(t *testing.T, test int, genesis string, expectBloc
} else {
// Force chain initialization
args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir}
geth := runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...)
geth.WaitExit()
runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...).WaitExit()
}
// Retrieve the DAO config flag from the database
path := filepath.Join(datadir, "geth", "chaindata")

View File

@@ -95,9 +95,9 @@ func (g *gethrpc) waitSynced() {
}
}
func startGethWithRpc(t *testing.T, name string, args ...string) *gethrpc {
func startGethWithIpc(t *testing.T, name string, args ...string) *gethrpc {
g := &gethrpc{name: name}
args = append([]string{"--networkid=42", "--port=0", "--nousb", "--http", "--http.port=0", "--http.api=admin,eth,les"}, args...)
args = append([]string{"--networkid=42", "--port=0", "--nousb"}, args...)
t.Logf("Starting %v with rpc: %v", name, args)
g.geth = runGeth(t, args...)
// wait before we can attach to it. TODO: probe for it properly
@@ -112,7 +112,7 @@ func startGethWithRpc(t *testing.T, name string, args ...string) *gethrpc {
}
func initGeth(t *testing.T) string {
g := runGeth(t, "--networkid=42", "init", "./testdata/clique.json")
g := runGeth(t, "--nousb", "--networkid=42", "init", "./testdata/clique.json")
datadir := g.Datadir
g.WaitExit()
return datadir
@@ -120,15 +120,15 @@ func initGeth(t *testing.T) string {
func startLightServer(t *testing.T) *gethrpc {
datadir := initGeth(t)
runGeth(t, "--datadir", datadir, "--password", "./testdata/password.txt", "account", "import", "./testdata/key.prv").WaitExit()
runGeth(t, "--nousb", "--datadir", datadir, "--password", "./testdata/password.txt", "account", "import", "./testdata/key.prv").WaitExit()
account := "0x02f0d131f1f97aef08aec6e3291b957d9efe7105"
server := startGethWithRpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1")
server := startGethWithIpc(t, "lightserver", "--allow-insecure-unlock", "--datadir", datadir, "--password", "./testdata/password.txt", "--unlock", account, "--mine", "--light.serve=100", "--light.maxpeers=1", "--nodiscover", "--nat=extip:127.0.0.1")
return server
}
func startClient(t *testing.T, name string) *gethrpc {
datadir := initGeth(t)
return startGethWithRpc(t, name, "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1")
return startGethWithIpc(t, name, "--datadir", datadir, "--nodiscover", "--syncmode=light", "--nat=extip:127.0.0.1")
}
func TestPriorityClient(t *testing.T) {
@@ -166,6 +166,7 @@ func TestPriorityClient(t *testing.T) {
freeCli.getNodeInfo().ID: freeCli,
prioCli.getNodeInfo().ID: prioCli,
}
time.Sleep(1 * time.Second)
lightServer.callRPC(&peers, "admin_peers")
peersWithNames := make(map[string]string)
for _, p := range peers {

View File

@@ -36,7 +36,8 @@ import (
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/node"
@@ -53,7 +54,7 @@ var (
gitCommit = ""
gitDate = ""
// The app that holds all commands and flags.
app = utils.NewApp(gitCommit, gitDate, "the go-ethereum command line interface")
app = flags.NewApp(gitCommit, gitDate, "the go-ethereum command line interface")
// flags that configure the node
nodeFlags = []cli.Flag{
utils.IdentityFlag,
@@ -98,6 +99,7 @@ var (
utils.LightEgressFlag,
utils.LightMaxPeersFlag,
utils.LegacyLightPeersFlag,
utils.LightNoPruneFlag,
utils.LightKDFFlag,
utils.UltraLightServersFlag,
utils.UltraLightFractionFlag,
@@ -106,6 +108,8 @@ var (
utils.CacheFlag,
utils.CacheDatabaseFlag,
utils.CacheTrieFlag,
utils.CacheTrieJournalFlag,
utils.CacheTrieRejournalFlag,
utils.CacheGCFlag,
utils.CacheSnapshotFlag,
utils.CacheNoPrefetchFlag,
@@ -167,8 +171,6 @@ var (
utils.LegacyRPCCORSDomainFlag,
utils.LegacyRPCVirtualHostsFlag,
utils.GraphQLEnabledFlag,
utils.GraphQLListenAddrFlag,
utils.GraphQLPortFlag,
utils.GraphQLCORSDomainFlag,
utils.GraphQLVirtualHostsFlag,
utils.HTTPApiFlag,
@@ -346,18 +348,20 @@ func geth(ctx *cli.Context) error {
if args := ctx.Args(); len(args) > 0 {
return fmt.Errorf("invalid command: %q", args[0])
}
prepare(ctx)
node := makeFullNode(ctx)
defer node.Close()
startNode(ctx, node)
node.Wait()
stack, backend := makeFullNode(ctx)
defer stack.Close()
startNode(ctx, stack, backend)
stack.Wait()
return nil
}
// startNode boots up the system node and all registered protocols, after which
// it unlocks any requested accounts, and starts the RPC/IPC interfaces and the
// miner.
func startNode(ctx *cli.Context, stack *node.Node) {
func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) {
debug.Memsize.Add("node", stack)
// Start up the node itself
@@ -377,25 +381,6 @@ func startNode(ctx *cli.Context, stack *node.Node) {
}
ethClient := ethclient.NewClient(rpcClient)
// Set contract backend for ethereum service if local node
// is serving LES requests.
if ctx.GlobalInt(utils.LegacyLightServFlag.Name) > 0 || ctx.GlobalInt(utils.LightServeFlag.Name) > 0 {
var ethService *eth.Ethereum
if err := stack.Service(&ethService); err != nil {
utils.Fatalf("Failed to retrieve ethereum service: %v", err)
}
ethService.SetContractBackend(ethClient)
}
// Set contract backend for les service if local node is
// running as a light client.
if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
var lesService *les.LightEthereum
if err := stack.Service(&lesService); err != nil {
utils.Fatalf("Failed to retrieve light ethereum service: %v", err)
}
lesService.SetContractBackend(ethClient)
}
go func() {
// Open any wallets already attached
for _, wallet := range stack.AccountManager().Wallets() {
@@ -447,7 +432,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
if timestamp := time.Unix(int64(done.Latest.Time), 0); time.Since(timestamp) < 10*time.Minute {
log.Info("Synchronisation completed", "latestnum", done.Latest.Number, "latesthash", done.Latest.Hash(),
"age", common.PrettyAge(timestamp))
stack.Stop()
stack.Close()
}
}
}()
@@ -459,24 +444,24 @@ func startNode(ctx *cli.Context, stack *node.Node) {
if ctx.GlobalString(utils.SyncModeFlag.Name) == "light" {
utils.Fatalf("Light clients do not support mining")
}
var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil {
ethBackend, ok := backend.(*eth.EthAPIBackend)
if !ok {
utils.Fatalf("Ethereum service not running: %v", err)
}
// Set the gas price to the limits from the CLI and start mining
gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
if ctx.GlobalIsSet(utils.LegacyMinerGasPriceFlag.Name) && !ctx.GlobalIsSet(utils.MinerGasPriceFlag.Name) {
gasprice = utils.GlobalBig(ctx, utils.LegacyMinerGasPriceFlag.Name)
}
ethereum.TxPool().SetGasPrice(gasprice)
ethBackend.TxPool().SetGasPrice(gasprice)
// start mining
threads := ctx.GlobalInt(utils.MinerThreadsFlag.Name)
if ctx.GlobalIsSet(utils.LegacyMinerThreadsFlag.Name) && !ctx.GlobalIsSet(utils.MinerThreadsFlag.Name) {
threads = ctx.GlobalInt(utils.LegacyMinerThreadsFlag.Name)
log.Warn("The flag --minerthreads is deprecated and will be removed in the future, please use --miner.threads")
}
if err := ethereum.StartMining(threads); err != nil {
if err := ethBackend.StartMining(threads); err != nil {
utils.Fatalf("Failed to start mining: %v", err)
}
}

View File

@@ -200,11 +200,11 @@ func (e *NoRewardEngine) Author(header *types.Header) (common.Address, error) {
return e.inner.Author(header)
}
func (e *NoRewardEngine) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
func (e *NoRewardEngine) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
return e.inner.VerifyHeader(chain, header, seal)
}
func (e *NoRewardEngine) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
func (e *NoRewardEngine) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
return e.inner.VerifyHeaders(chain, headers, seals)
}
@@ -212,11 +212,11 @@ func (e *NoRewardEngine) VerifyUncles(chain consensus.ChainReader, block *types.
return e.inner.VerifyUncles(chain, block)
}
func (e *NoRewardEngine) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
func (e *NoRewardEngine) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
return e.inner.VerifySeal(chain, header)
}
func (e *NoRewardEngine) Prepare(chain consensus.ChainReader, header *types.Header) error {
func (e *NoRewardEngine) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
return e.inner.Prepare(chain, header)
}
@@ -229,7 +229,7 @@ func (e *NoRewardEngine) accumulateRewards(config *params.ChainConfig, state *st
state.AddBalance(header.Coinbase, reward)
}
func (e *NoRewardEngine) Finalize(chain consensus.ChainReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction,
func (e *NoRewardEngine) Finalize(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction,
uncles []*types.Header) {
if e.rewardsOn {
e.inner.Finalize(chain, header, statedb, txs, uncles)
@@ -239,7 +239,7 @@ func (e *NoRewardEngine) Finalize(chain consensus.ChainReader, header *types.Hea
}
}
func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction,
func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, statedb *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
if e.rewardsOn {
return e.inner.FinalizeAndAssemble(chain, header, statedb, txs, uncles, receipts)
@@ -248,11 +248,11 @@ func (e *NoRewardEngine) FinalizeAndAssemble(chain consensus.ChainReader, header
header.Root = statedb.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Header seems complete, assemble into a block and return
return types.NewBlock(header, txs, uncles, receipts), nil
return types.NewBlock(header, txs, uncles, receipts, new(trie.Trie)), nil
}
}
func (e *NoRewardEngine) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
func (e *NoRewardEngine) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
return e.inner.Seal(chain, block, results, stop)
}
@@ -260,11 +260,11 @@ func (e *NoRewardEngine) SealHash(header *types.Header) common.Hash {
return e.inner.SealHash(header)
}
func (e *NoRewardEngine) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
func (e *NoRewardEngine) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return e.inner.CalcDifficulty(chain, time, parent)
}
func (e *NoRewardEngine) APIs(chain consensus.ChainReader) []rpc.API {
func (e *NoRewardEngine) APIs(chain consensus.ChainHeaderReader) []rpc.API {
return e.inner.APIs(chain)
}

View File

@@ -24,44 +24,12 @@ import (
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/flags"
cli "gopkg.in/urfave/cli.v1"
)
// AppHelpTemplate is the test template for the default, global app help topic.
var AppHelpTemplate = `NAME:
{{.App.Name}} - {{.App.Usage}}
Copyright 2013-2019 The go-ethereum Authors
USAGE:
{{.App.HelpName}} [options]{{if .App.Commands}} command [command options]{{end}} {{if .App.ArgsUsage}}{{.App.ArgsUsage}}{{else}}[arguments...]{{end}}
{{if .App.Version}}
VERSION:
{{.App.Version}}
{{end}}{{if len .App.Authors}}
AUTHOR(S):
{{range .App.Authors}}{{ . }}{{end}}
{{end}}{{if .App.Commands}}
COMMANDS:
{{range .App.Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .FlagGroups}}
{{range .FlagGroups}}{{.Name}} OPTIONS:
{{range .Flags}}{{.}}
{{end}}
{{end}}{{end}}{{if .App.Copyright }}
COPYRIGHT:
{{.App.Copyright}}
{{end}}
`
// flagGroup is a collection of flags belonging to a single topic.
type flagGroup struct {
Name string
Flags []cli.Flag
}
// AppHelpFlagGroups is the application flags, grouped by functionality.
var AppHelpFlagGroups = []flagGroup{
var AppHelpFlagGroups = []flags.FlagGroup{
{
Name: "ETHEREUM",
Flags: []cli.Flag{
@@ -96,6 +64,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.UltraLightServersFlag,
utils.UltraLightFractionFlag,
utils.UltraLightOnlyAnnounceFlag,
utils.LightNoPruneFlag,
},
},
{
@@ -140,6 +109,8 @@ var AppHelpFlagGroups = []flagGroup{
utils.CacheFlag,
utils.CacheDatabaseFlag,
utils.CacheTrieFlag,
utils.CacheTrieJournalFlag,
utils.CacheTrieRejournalFlag,
utils.CacheGCFlag,
utils.CacheSnapshotFlag,
utils.CacheNoPrefetchFlag,
@@ -171,8 +142,6 @@ var AppHelpFlagGroups = []flagGroup{
utils.WSApiFlag,
utils.WSAllowedOriginsFlag,
utils.GraphQLEnabledFlag,
utils.GraphQLListenAddrFlag,
utils.GraphQLPortFlag,
utils.GraphQLCORSDomainFlag,
utils.GraphQLVirtualHostsFlag,
utils.RPCGlobalGasCap,
@@ -242,7 +211,7 @@ var AppHelpFlagGroups = []flagGroup{
Flags: metricsFlags,
},
{
Name: "WHISPER (EXPERIMENTAL)",
Name: "WHISPER (deprecated)",
Flags: whisperFlags,
},
{
@@ -260,6 +229,8 @@ var AppHelpFlagGroups = []flagGroup{
utils.LegacyWSApiFlag,
utils.LegacyGpoBlocksFlag,
utils.LegacyGpoPercentileFlag,
utils.LegacyGraphQLListenAddrFlag,
utils.LegacyGraphQLPortFlag,
}, debug.DeprecatedFlags...),
},
{
@@ -271,53 +242,14 @@ var AppHelpFlagGroups = []flagGroup{
},
}
// byCategory sorts an array of flagGroup by Name in the order
// defined in AppHelpFlagGroups.
type byCategory []flagGroup
func (a byCategory) Len() int { return len(a) }
func (a byCategory) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a byCategory) Less(i, j int) bool {
iCat, jCat := a[i].Name, a[j].Name
iIdx, jIdx := len(AppHelpFlagGroups), len(AppHelpFlagGroups) // ensure non categorized flags come last
for i, group := range AppHelpFlagGroups {
if iCat == group.Name {
iIdx = i
}
if jCat == group.Name {
jIdx = i
}
}
return iIdx < jIdx
}
func flagCategory(flag cli.Flag) string {
for _, category := range AppHelpFlagGroups {
for _, flg := range category.Flags {
if flg.GetName() == flag.GetName() {
return category.Name
}
}
}
return "MISC"
}
func init() {
// Override the default app help template
cli.AppHelpTemplate = AppHelpTemplate
// Define a one shot struct to pass to the usage template
type helpData struct {
App interface{}
FlagGroups []flagGroup
}
cli.AppHelpTemplate = flags.AppHelpTemplate
// Override the default app help printer, but only for the global app help
originalHelpPrinter := cli.HelpPrinter
cli.HelpPrinter = func(w io.Writer, tmpl string, data interface{}) {
if tmpl == AppHelpTemplate {
if tmpl == flags.AppHelpTemplate {
// Iterate over all the flags and add any uncategorized ones
categorized := make(map[string]struct{})
for _, group := range AppHelpFlagGroups {
@@ -349,22 +281,22 @@ func init() {
}()
}
// Render out custom usage screen
originalHelpPrinter(w, tmpl, helpData{data, AppHelpFlagGroups})
} else if tmpl == utils.CommandHelpTemplate {
originalHelpPrinter(w, tmpl, flags.HelpData{App: data, FlagGroups: AppHelpFlagGroups})
} else if tmpl == flags.CommandHelpTemplate {
// Iterate over all command specific flags and categorize them
categorized := make(map[string][]cli.Flag)
for _, flag := range data.(cli.Command).Flags {
if _, ok := categorized[flag.String()]; !ok {
categorized[flagCategory(flag)] = append(categorized[flagCategory(flag)], flag)
categorized[flags.FlagCategory(flag, AppHelpFlagGroups)] = append(categorized[flags.FlagCategory(flag, AppHelpFlagGroups)], flag)
}
}
// sort to get a stable ordering
sorted := make([]flagGroup, 0, len(categorized))
sorted := make([]flags.FlagGroup, 0, len(categorized))
for cat, flgs := range categorized {
sorted = append(sorted, flagGroup{cat, flgs})
sorted = append(sorted, flags.FlagGroup{Name: cat, Flags: flgs})
}
sort.Sort(byCategory(sorted))
sort.Sort(flags.ByCategory(sorted))
// add sorted array to data and render with default printer
originalHelpPrinter(w, tmpl, map[string]interface{}{

View File

@@ -289,7 +289,7 @@ func createNode(ctx *cli.Context) error {
config.PrivateKey = privKey
}
if services := ctx.String("services"); services != "" {
config.Services = strings.Split(services, ",")
config.Lifecycles = strings.Split(services, ",")
}
node, err := client.CreateNode(config)
if err != nil {

View File

@@ -73,7 +73,7 @@ func StartNode(stack *node.Node) {
defer signal.Stop(sigc)
<-sigc
log.Info("Got interrupt, shutting down...")
go stack.Stop()
go stack.Close()
for i := 10; i > 0; i-- {
<-sigc
if i > 1 {

View File

@@ -19,7 +19,6 @@ package utils
import (
"crypto/ecdsa"
"errors"
"fmt"
"io"
"io/ioutil"
@@ -40,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
@@ -48,6 +48,8 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethstats"
"github.com/ethereum/go-ethereum/graphql"
"github.com/ethereum/go-ethereum/internal/ethapi"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
@@ -61,36 +63,10 @@ import (
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
pcsclite "github.com/gballet/go-libpcsclite"
cli "gopkg.in/urfave/cli.v1"
)
var (
CommandHelpTemplate = `{{.cmd.Name}}{{if .cmd.Subcommands}} command{{end}}{{if .cmd.Flags}} [command options]{{end}} [arguments...]
{{if .cmd.Description}}{{.cmd.Description}}
{{end}}{{if .cmd.Subcommands}}
SUBCOMMANDS:
{{range .cmd.Subcommands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
{{end}}{{end}}{{if .categorizedFlags}}
{{range $idx, $categorized := .categorizedFlags}}{{$categorized.Name}} OPTIONS:
{{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}} {{.}}
{{end}}
{{end}}`
)
func init() {
cli.AppHelpTemplate = `{{.Name}} {{if .Flags}}[global options] {{end}}command{{if .Flags}} [command options]{{end}} [arguments...]
@@ -104,21 +80,10 @@ GLOBAL OPTIONS:
{{range .Flags}}{{.}}
{{end}}{{end}}
`
cli.CommandHelpTemplate = CommandHelpTemplate
cli.CommandHelpTemplate = flags.CommandHelpTemplate
cli.HelpPrinter = printHelp
}
// NewApp creates an app with sane defaults.
func NewApp(gitCommit, gitDate, usage string) *cli.App {
app := cli.NewApp()
app.Name = filepath.Base(os.Args[0])
app.Author = ""
app.Email = ""
app.Version = params.VersionWithCommit(gitCommit, gitDate)
app.Usage = usage
return app
}
func printHelp(out io.Writer, templ string, data interface{}) {
funcMap := template.FuncMap{"join": strings.Join}
t := template.Must(template.New("help").Funcs(funcMap).Parse(templ))
@@ -282,6 +247,10 @@ var (
Name: "ulc.onlyannounce",
Usage: "Ultra light server sends announcements only",
}
LightNoPruneFlag = cli.BoolFlag{
Name: "light.nopruning",
Usage: "Disable ancient light chain data pruning",
}
// Ethash settings
EthashCacheDirFlag = DirectoryFlag{
Name: "ethash.cachedir",
@@ -390,6 +359,16 @@ var (
Usage: "Percentage of cache memory allowance to use for trie caching (default = 15% full mode, 30% archive mode)",
Value: 15,
}
CacheTrieJournalFlag = cli.StringFlag{
Name: "cache.trie.journal",
Usage: "Disk journal directory for trie cache to survive node restarts",
Value: eth.DefaultConfig.TrieCleanCacheJournal,
}
CacheTrieRejournalFlag = cli.DurationFlag{
Name: "cache.trie.rejournal",
Usage: "Time interval to regenerate the trie cache journal",
Value: eth.DefaultConfig.TrieCleanCacheRejournal,
}
CacheGCFlag = cli.IntFlag{
Name: "cache.gc",
Usage: "Percentage of cache memory allowance to use for trie pruning (default = 25% full mode, 0% archive mode)",
@@ -536,6 +515,20 @@ var (
Usage: "API's offered over the HTTP-RPC interface",
Value: "",
}
GraphQLEnabledFlag = cli.BoolFlag{
Name: "graphql",
Usage: "Enable GraphQL on the HTTP-RPC server. Note that GraphQL can only be started if an HTTP server is started as well.",
}
GraphQLCORSDomainFlag = cli.StringFlag{
Name: "graphql.corsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: "",
}
GraphQLVirtualHostsFlag = cli.StringFlag{
Name: "graphql.vhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: strings.Join(node.DefaultConfig.GraphQLVirtualHosts, ","),
}
WSEnabledFlag = cli.BoolFlag{
Name: "ws",
Usage: "Enable the WS-RPC server",
@@ -560,30 +553,6 @@ var (
Usage: "Origins from which to accept websockets requests",
Value: "",
}
GraphQLEnabledFlag = cli.BoolFlag{
Name: "graphql",
Usage: "Enable the GraphQL server",
}
GraphQLListenAddrFlag = cli.StringFlag{
Name: "graphql.addr",
Usage: "GraphQL server listening interface",
Value: node.DefaultGraphQLHost,
}
GraphQLPortFlag = cli.IntFlag{
Name: "graphql.port",
Usage: "GraphQL server listening port",
Value: node.DefaultGraphQLPort,
}
GraphQLCORSDomainFlag = cli.StringFlag{
Name: "graphql.corsdomain",
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: "",
}
GraphQLVirtualHostsFlag = cli.StringFlag{
Name: "graphql.vhosts",
Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
Value: strings.Join(node.DefaultConfig.GraphQLVirtualHosts, ","),
}
ExecFlag = cli.StringFlag{
Name: "exec",
Usage: "Execute JavaScript statement",
@@ -669,12 +638,12 @@ var (
WhisperMaxMessageSizeFlag = cli.IntFlag{
Name: "shh.maxmessagesize",
Usage: "Max message size accepted",
Value: int(whisper.DefaultMaxMessageSize),
Value: 1024 * 1024,
}
WhisperMinPOWFlag = cli.Float64Flag{
Name: "shh.pow",
Usage: "Minimum POW accepted",
Value: whisper.DefaultMinimumPoW,
Value: 0.2,
}
WhisperRestrictConnectionBetweenLightClientsFlag = cli.BoolFlag{
Name: "shh.restrict-light",
@@ -970,13 +939,6 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
// setGraphQL creates the GraphQL listener interface string from the set
// command line flags, returning empty if the GraphQL endpoint is disabled.
func setGraphQL(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalBool(GraphQLEnabledFlag.Name) && cfg.GraphQLHost == "" {
cfg.GraphQLHost = "127.0.0.1"
if ctx.GlobalIsSet(GraphQLListenAddrFlag.Name) {
cfg.GraphQLHost = ctx.GlobalString(GraphQLListenAddrFlag.Name)
}
}
cfg.GraphQLPort = ctx.GlobalInt(GraphQLPortFlag.Name)
if ctx.GlobalIsSet(GraphQLCORSDomainFlag.Name) {
cfg.GraphQLCors = splitAndTrim(ctx.GlobalString(GraphQLCORSDomainFlag.Name))
}
@@ -1070,6 +1032,9 @@ func setLes(ctx *cli.Context, cfg *eth.Config) {
if ctx.GlobalIsSet(UltraLightOnlyAnnounceFlag.Name) {
cfg.UltraLightOnlyAnnounce = ctx.GlobalBool(UltraLightOnlyAnnounceFlag.Name)
}
if ctx.GlobalIsSet(LightNoPruneFlag.Name) {
cfg.LightNoPrune = ctx.GlobalBool(LightNoPruneFlag.Name)
}
}
// makeDatabaseHandles raises out the number of allowed file handles per process
@@ -1499,15 +1464,12 @@ func CheckExclusive(ctx *cli.Context, args ...interface{}) {
}
// SetShhConfig applies shh-related command line flags to the config.
func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
if ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) {
cfg.MaxMessageSize = uint32(ctx.GlobalUint(WhisperMaxMessageSizeFlag.Name))
}
if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) {
cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name)
}
if ctx.GlobalIsSet(WhisperRestrictConnectionBetweenLightClientsFlag.Name) {
cfg.RestrictConnectionBetweenLightClients = true
func SetShhConfig(ctx *cli.Context, stack *node.Node) {
if ctx.GlobalIsSet(WhisperEnabledFlag.Name) ||
ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) ||
ctx.GlobalIsSet(WhisperMinPOWFlag.Name) ||
ctx.GlobalIsSet(WhisperRestrictConnectionBetweenLightClientsFlag.Name) {
log.Warn("Whisper support has been deprecated and the code has been moved to github.com/ethereum/whisper")
}
}
@@ -1563,6 +1525,12 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheTrieFlag.Name) {
cfg.TrieCleanCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheTrieFlag.Name) / 100
}
if ctx.GlobalIsSet(CacheTrieJournalFlag.Name) {
cfg.TrieCleanCacheJournal = ctx.GlobalString(CacheTrieJournalFlag.Name)
}
if ctx.GlobalIsSet(CacheTrieRejournalFlag.Name) {
cfg.TrieCleanCacheRejournal = ctx.GlobalDuration(CacheTrieRejournalFlag.Name)
}
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
cfg.TrieDirtyCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
}
@@ -1570,6 +1538,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.SnapshotCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheSnapshotFlag.Name) / 100
}
if !ctx.GlobalIsSet(SnapshotFlag.Name) {
cfg.TrieCleanCache += cfg.SnapshotCache
cfg.SnapshotCache = 0 // Disabled
}
if ctx.GlobalIsSet(DocRootFlag.Name) {
@@ -1638,23 +1607,43 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
}
// Create new developer account or reuse existing one
var (
developer accounts.Account
err error
developer accounts.Account
passphrase string
err error
)
if accs := ks.Accounts(); len(accs) > 0 {
if list := MakePasswordList(ctx); len(list) > 0 {
// Just take the first value. Although the function returns a possible multiple values and
// some usages iterate through them as attempts, that doesn't make sense in this setting,
// when we're definitely concerned with only one account.
passphrase = list[0]
}
// setEtherbase has been called above, configuring the miner address from command line flags.
if cfg.Miner.Etherbase != (common.Address{}) {
developer = accounts.Account{Address: cfg.Miner.Etherbase}
} else if accs := ks.Accounts(); len(accs) > 0 {
developer = ks.Accounts()[0]
} else {
developer, err = ks.NewAccount("")
developer, err = ks.NewAccount(passphrase)
if err != nil {
Fatalf("Failed to create developer account: %v", err)
}
}
if err := ks.Unlock(developer, ""); err != nil {
if err := ks.Unlock(developer, passphrase); err != nil {
Fatalf("Failed to unlock developer account: %v", err)
}
log.Info("Using developer account", "address", developer.Address)
// Create a new developer genesis block or reuse existing one
cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.GlobalInt(DeveloperPeriodFlag.Name)), developer.Address)
if ctx.GlobalIsSet(DataDirFlag.Name) {
// Check if we have an already initialized chain and fall back to
// that if so. Otherwise we need to generate a new genesis spec.
chaindb := MakeChainDatabase(ctx, stack)
if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
cfg.Genesis = nil // fallback to db content
}
chaindb.Close()
}
if !ctx.GlobalIsSet(MinerGasPriceFlag.Name) && !ctx.GlobalIsSet(LegacyMinerGasPriceFlag.Name) {
cfg.Miner.GasPrice = big.NewInt(1)
}
@@ -1682,70 +1671,39 @@ func setDNSDiscoveryDefaults(cfg *eth.Config, genesis common.Hash) {
}
// RegisterEthService adds an Ethereum client to the stack.
func RegisterEthService(stack *node.Node, cfg *eth.Config) {
var err error
func RegisterEthService(stack *node.Node, cfg *eth.Config) ethapi.Backend {
if cfg.SyncMode == downloader.LightSync {
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return les.New(ctx, cfg)
})
backend, err := les.New(stack, cfg)
if err != nil {
Fatalf("Failed to register the Ethereum service: %v", err)
}
return backend.ApiBackend
} else {
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
fullNode, err := eth.New(ctx, cfg)
if fullNode != nil && cfg.LightServ > 0 {
ls, _ := les.NewLesServer(fullNode, cfg)
fullNode.AddLesServer(ls)
backend, err := eth.New(stack, cfg)
if err != nil {
Fatalf("Failed to register the Ethereum service: %v", err)
}
if cfg.LightServ > 0 {
_, err := les.NewLesServer(stack, backend, cfg)
if err != nil {
Fatalf("Failed to create the LES server: %v", err)
}
return fullNode, err
})
}
if err != nil {
Fatalf("Failed to register the Ethereum service: %v", err)
}
}
// 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) {
return whisper.New(cfg), nil
}); err != nil {
Fatalf("Failed to register the Whisper service: %v", err)
}
return backend.APIBackend
}
}
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to
// the given node.
func RegisterEthStatsService(stack *node.Node, url string) {
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
// Retrieve both eth and les services
var ethServ *eth.Ethereum
ctx.Service(&ethServ)
var lesServ *les.LightEthereum
ctx.Service(&lesServ)
// Let ethstats use whichever is not nil
return ethstats.New(url, ethServ, lesServ)
}); err != nil {
func RegisterEthStatsService(stack *node.Node, backend ethapi.Backend, url string) {
if err := ethstats.New(stack, backend, backend.Engine(), url); err != nil {
Fatalf("Failed to register the Ethereum Stats service: %v", err)
}
}
// RegisterGraphQLService is a utility function to construct a new service and register it against a node.
func RegisterGraphQLService(stack *node.Node, endpoint string, cors, vhosts []string, timeouts rpc.HTTPTimeouts) {
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
// Try to construct the GraphQL service backed by a full node
var ethServ *eth.Ethereum
if err := ctx.Service(&ethServ); err == nil {
return graphql.New(ethServ.APIBackend, endpoint, cors, vhosts, timeouts)
}
// Try to construct the GraphQL service backed by a light node
var lesServ *les.LightEthereum
if err := ctx.Service(&lesServ); err == nil {
return graphql.New(lesServ.ApiBackend, endpoint, cors, vhosts, timeouts)
}
// Well, this should not have happened, bail out
return nil, errors.New("no Ethereum service")
}); err != nil {
func RegisterGraphQLService(stack *node.Node, backend ethapi.Backend, cfg node.Config) {
if err := graphql.New(stack, backend, cfg.GraphQLCors, cfg.GraphQLVirtualHosts); err != nil {
Fatalf("Failed to register the GraphQL service: %v", err)
}
}
@@ -1800,12 +1758,17 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
var (
cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
handles = makeDatabaseHandles()
err error
chainDb ethdb.Database
)
name := "chaindata"
if ctx.GlobalString(SyncModeFlag.Name) == "light" {
name = "lightchaindata"
name := "lightchaindata"
chainDb, err = stack.OpenDatabase(name, cache, handles, "")
} else {
name := "chaindata"
chainDb, err = stack.OpenDatabaseWithFreezer(name, cache, handles, ctx.GlobalString(AncientFlag.Name), "")
}
chainDb, err := stack.OpenDatabaseWithFreezer(name, cache, handles, ctx.GlobalString(AncientFlag.Name), "")
if err != nil {
Fatalf("Could not open database: %v", err)
}

View File

@@ -89,6 +89,8 @@ var (
Name: "testnet",
Usage: "Pre-configured test network (Deprecated: Please choose one of --goerli, --rinkeby, or --ropsten.)",
}
// (Deprecated May 2020, shown in aliased flags section)
LegacyRPCEnabledFlag = cli.BoolFlag{
Name: "rpc",
Usage: "Enable the HTTP-RPC server (deprecated, use --http)",
@@ -158,6 +160,17 @@ var (
Usage: "Comma separated enode URLs for P2P v5 discovery bootstrap (light server, light nodes) (deprecated, use --bootnodes)",
Value: "",
}
// (Deprecated July 2020, shown in aliased flags section)
LegacyGraphQLListenAddrFlag = cli.StringFlag{
Name: "graphql.addr",
Usage: "GraphQL server listening interface (deprecated, graphql can only be enabled on the HTTP-RPC server endpoint, use --graphql)",
}
LegacyGraphQLPortFlag = cli.IntFlag{
Name: "graphql.port",
Usage: "GraphQL server listening port (deprecated, graphql can only be enabled on the HTTP-RPC server endpoint, use --graphql)",
Value: node.DefaultHTTPPort,
}
)
// showDeprecated displays deprecated flags that will be soon removed from the codebase.

View File

@@ -1,774 +0,0 @@
// Copyright 2017 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/>.
// This is a simple Whisper node. It could be used as a stand-alone bootstrap node.
// Also, could be used for different test and diagnostics purposes.
package main
import (
"bufio"
"crypto/ecdsa"
crand "crypto/rand"
"crypto/sha512"
"encoding/binary"
"encoding/hex"
"flag"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"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/prompt"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/whisper/mailserver"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"golang.org/x/crypto/pbkdf2"
)
const quitCommand = "~Q"
const entropySize = 32
// singletons
var (
server *p2p.Server
shh *whisper.Whisper
done chan struct{}
mailServer mailserver.WMailServer
entropy [entropySize]byte
input = bufio.NewReader(os.Stdin)
)
// encryption
var (
symKey []byte
pub *ecdsa.PublicKey
asymKey *ecdsa.PrivateKey
nodeid *ecdsa.PrivateKey
topic whisper.TopicType
asymKeyID string
asymFilterID string
symFilterID string
symPass string
msPassword string
)
// cmd arguments
var (
bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't initiate connection to peers, just wait for incoming connections")
forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither encrypt nor decrypt messages")
mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand")
requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server")
asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption")
generateKey = flag.Bool("generatekey", false, "generate and show the private key")
fileExMode = flag.Bool("fileexchange", false, "file exchange mode")
fileReader = flag.Bool("filereader", false, "load and decrypt messages saved as files, display as plain text")
testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics (password, etc.)")
echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level")
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
argWorkTime = flag.Uint("work", 5, "work time in seconds")
argMaxSize = flag.Uint("maxsize", uint(whisper.DefaultMaxMessageSize), "max size of message")
argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request")
argIP = flag.String("ip", "", "IP address and port of this node (e.g. 127.0.0.1:30303)")
argPub = flag.String("pub", "", "public key for asymmetric encryption")
argDBPath = flag.String("dbpath", "", "path to the server's DB directory")
argIDFile = flag.String("idfile", "", "file name with node id (private key)")
argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
argSaveDir = flag.String("savedir", "", "directory where all incoming messages will be saved as files")
)
func main() {
processArgs()
initialize()
run()
shutdown()
}
func processArgs() {
flag.Parse()
if len(*argIDFile) > 0 {
var err error
nodeid, err = crypto.LoadECDSA(*argIDFile)
if err != nil {
utils.Fatalf("Failed to load file [%s]: %s.", *argIDFile, err)
}
}
const enodePrefix = "enode://"
if len(*argEnode) > 0 {
if (*argEnode)[:len(enodePrefix)] != enodePrefix {
*argEnode = enodePrefix + *argEnode
}
}
if len(*argTopic) > 0 {
x, err := hex.DecodeString(*argTopic)
if err != nil {
utils.Fatalf("Failed to parse the topic: %s", err)
}
topic = whisper.BytesToTopic(x)
}
if *asymmetricMode && len(*argPub) > 0 {
var err error
if pub, err = crypto.UnmarshalPubkey(common.FromHex(*argPub)); err != nil {
utils.Fatalf("invalid public key")
}
}
if len(*argSaveDir) > 0 {
if _, err := os.Stat(*argSaveDir); os.IsNotExist(err) {
utils.Fatalf("Download directory '%s' does not exist", *argSaveDir)
}
} else if *fileExMode {
utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode")
}
if *echoMode {
echo()
}
}
func echo() {
fmt.Printf("ttl = %d \n", *argTTL)
fmt.Printf("workTime = %d \n", *argWorkTime)
fmt.Printf("pow = %f \n", *argPoW)
fmt.Printf("mspow = %f \n", *argServerPoW)
fmt.Printf("ip = %s \n", *argIP)
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)
}
func initialize() {
log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(*argVerbosity), log.StreamHandler(os.Stderr, log.TerminalFormat(false))))
done = make(chan struct{})
var peers []*enode.Node
var err error
if *generateKey {
key, err := crypto.GenerateKey()
if err != nil {
utils.Fatalf("Failed to generate private key: %s", err)
}
k := hex.EncodeToString(crypto.FromECDSA(key))
fmt.Printf("Random private key: %s \n", k)
os.Exit(0)
}
if *testMode {
symPass = "wwww" // ascii code: 0x77777777
msPassword = "wwww"
}
if *bootstrapMode {
if len(*argIP) == 0 {
argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ")
}
} else if *fileReader {
*bootstrapMode = true
} else {
if len(*argEnode) == 0 {
argEnode = scanLineA("Please enter the peer's enode: ")
}
peer := enode.MustParse(*argEnode)
peers = append(peers, peer)
}
if *mailServerMode {
if len(msPassword) == 0 {
msPassword, err = prompt.Stdin.PromptPassword("Please enter the Mail Server password: ")
if err != nil {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
}
cfg := &whisper.Config{
MaxMessageSize: uint32(*argMaxSize),
MinimumAcceptedPOW: *argPoW,
}
shh = whisper.New(cfg)
if *argPoW != whisper.DefaultMinimumPoW {
err := shh.SetMinimumPoW(*argPoW)
if err != nil {
utils.Fatalf("Failed to set PoW: %s", err)
}
}
if uint32(*argMaxSize) != whisper.DefaultMaxMessageSize {
err := shh.SetMaxMessageSize(uint32(*argMaxSize))
if err != nil {
utils.Fatalf("Failed to set max message size: %s", err)
}
}
asymKeyID, err = shh.NewKeyPair()
if err != nil {
utils.Fatalf("Failed to generate a new key pair: %s", err)
}
asymKey, err = shh.GetPrivateKey(asymKeyID)
if err != nil {
utils.Fatalf("Failed to retrieve a new key pair: %s", err)
}
if nodeid == nil {
tmpID, err := shh.NewKeyPair()
if err != nil {
utils.Fatalf("Failed to generate a new key pair: %s", err)
}
nodeid, err = shh.GetPrivateKey(tmpID)
if err != nil {
utils.Fatalf("Failed to retrieve a new key pair: %s", err)
}
}
maxPeers := 80
if *bootstrapMode {
maxPeers = 800
}
_, err = crand.Read(entropy[:])
if err != nil {
utils.Fatalf("crypto/rand failed: %s", err)
}
if *mailServerMode {
shh.RegisterServer(&mailServer)
if err := mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW); err != nil {
utils.Fatalf("Failed to init MailServer: %s", err)
}
}
server = &p2p.Server{
Config: p2p.Config{
PrivateKey: nodeid,
MaxPeers: maxPeers,
Name: common.MakeName("wnode", "6.0"),
Protocols: shh.Protocols(),
ListenAddr: *argIP,
NAT: nat.Any(),
BootstrapNodes: peers,
StaticNodes: peers,
TrustedNodes: peers,
},
}
}
func startServer() error {
err := server.Start()
if err != nil {
fmt.Printf("Failed to start Whisper peer: %s.", err)
return err
}
fmt.Printf("my public key: %s \n", hexutil.Encode(crypto.FromECDSAPub(&asymKey.PublicKey)))
fmt.Println(server.NodeInfo().Enode)
if *bootstrapMode {
configureNode()
fmt.Println("Bootstrap Whisper node started")
} else {
fmt.Println("Whisper node started")
// first see if we can establish connection, then ask for user input
waitForConnection(true)
configureNode()
}
if *fileExMode {
fmt.Printf("Please type the file name to be send. To quit type: '%s'\n", quitCommand)
} else if *fileReader {
fmt.Printf("Please type the file name to be decrypted. To quit type: '%s'\n", quitCommand)
} else if !*forwarderMode {
fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand)
}
return nil
}
func configureNode() {
var err error
var p2pAccept bool
if *forwarderMode {
return
}
if *asymmetricMode {
if len(*argPub) == 0 {
s := scanLine("Please enter the peer's public key: ")
b := common.FromHex(s)
if b == nil {
utils.Fatalf("Error: can not convert hexadecimal string")
}
if pub, err = crypto.UnmarshalPubkey(b); err != nil {
utils.Fatalf("Error: invalid peer public key")
}
}
}
if *requestMail {
p2pAccept = true
if len(msPassword) == 0 {
msPassword, err = prompt.Stdin.PromptPassword("Please enter the Mail Server password: ")
if err != nil {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
}
if !*asymmetricMode && !*forwarderMode {
if len(symPass) == 0 {
symPass, err = prompt.Stdin.PromptPassword("Please enter the password for symmetric encryption: ")
if err != nil {
utils.Fatalf("Failed to read password: %v", err)
}
}
symKeyID, err := shh.AddSymKeyFromPassword(symPass)
if err != nil {
utils.Fatalf("Failed to create symmetric key: %s", err)
}
symKey, err = shh.GetSymKey(symKeyID)
if err != nil {
utils.Fatalf("Failed to save symmetric key: %s", err)
}
if len(*argTopic) == 0 {
generateTopic([]byte(symPass))
}
fmt.Printf("Filter is configured for the topic: %x \n", topic)
}
if *mailServerMode {
if len(*argDBPath) == 0 {
argDBPath = scanLineA("Please enter the path to DB file: ")
}
}
symFilter := whisper.Filter{
KeySym: symKey,
Topics: [][]byte{topic[:]},
AllowP2P: p2pAccept,
}
symFilterID, err = shh.Subscribe(&symFilter)
if err != nil {
utils.Fatalf("Failed to install filter: %s", err)
}
asymFilter := whisper.Filter{
KeyAsym: asymKey,
Topics: [][]byte{topic[:]},
AllowP2P: p2pAccept,
}
asymFilterID, err = shh.Subscribe(&asymFilter)
if err != nil {
utils.Fatalf("Failed to install filter: %s", err)
}
}
func generateTopic(password []byte) {
x := pbkdf2.Key(password, password, 4096, 128, sha512.New)
for i := 0; i < len(x); i++ {
topic[i%whisper.TopicLength] ^= x[i]
}
}
func waitForConnection(timeout bool) {
var cnt int
var connected bool
for !connected {
time.Sleep(time.Millisecond * 50)
connected = server.PeerCount() > 0
if timeout {
cnt++
if cnt > 1000 {
utils.Fatalf("Timeout expired, failed to connect")
}
}
}
fmt.Println("Connected to peer.")
}
func run() {
err := startServer()
if err != nil {
return
}
defer server.Stop()
shh.Start(nil)
defer shh.Stop()
if !*forwarderMode {
go messageLoop()
}
if *requestMail {
requestExpiredMessagesLoop()
} else if *fileExMode {
sendFilesLoop()
} else if *fileReader {
fileReaderLoop()
} else {
sendLoop()
}
}
func shutdown() {
close(done)
mailServer.Close()
}
func sendLoop() {
for {
s := scanLine("")
if s == quitCommand {
fmt.Println("Quit command received")
return
}
sendMsg([]byte(s))
if *asymmetricMode {
// print your own message for convenience,
// because in asymmetric mode it is impossible to decrypt it
timestamp := time.Now().Unix()
from := crypto.PubkeyToAddress(asymKey.PublicKey)
fmt.Printf("\n%d <%x>: %s\n", timestamp, from, s)
}
}
}
func sendFilesLoop() {
for {
s := scanLine("")
if s == quitCommand {
fmt.Println("Quit command received")
return
}
b, err := ioutil.ReadFile(s)
if err != nil {
fmt.Printf(">>> Error: %s \n", err)
} else {
h := sendMsg(b)
if (h == common.Hash{}) {
fmt.Printf(">>> Error: message was not sent \n")
} else {
timestamp := time.Now().Unix()
from := crypto.PubkeyToAddress(asymKey.PublicKey)
fmt.Printf("\n%d <%x>: sent message with hash %x\n", timestamp, from, h)
}
}
}
}
func fileReaderLoop() {
watcher1 := shh.GetFilter(symFilterID)
watcher2 := shh.GetFilter(asymFilterID)
if watcher1 == nil && watcher2 == nil {
fmt.Println("Error: neither symmetric nor asymmetric filter is installed")
return
}
for {
s := scanLine("")
if s == quitCommand {
fmt.Println("Quit command received")
return
}
raw, err := ioutil.ReadFile(s)
if err != nil {
fmt.Printf(">>> Error: %s \n", err)
} else {
env := whisper.Envelope{Data: raw} // the topic is zero
msg := env.Open(watcher1) // force-open envelope regardless of the topic
if msg == nil {
msg = env.Open(watcher2)
}
if msg == nil {
fmt.Printf(">>> Error: failed to decrypt the message \n")
} else {
printMessageInfo(msg)
}
}
}
}
func scanLine(prompt string) string {
if len(prompt) > 0 {
fmt.Print(prompt)
}
txt, err := input.ReadString('\n')
if err != nil {
utils.Fatalf("input error: %s", err)
}
txt = strings.TrimRight(txt, "\n\r")
return txt
}
func scanLineA(prompt string) *string {
s := scanLine(prompt)
return &s
}
func scanUint(prompt string) uint32 {
s := scanLine(prompt)
i, err := strconv.Atoi(s)
if err != nil {
utils.Fatalf("Fail to parse the lower time limit: %s", err)
}
return uint32(i)
}
func sendMsg(payload []byte) common.Hash {
params := whisper.MessageParams{
Src: asymKey,
Dst: pub,
KeySym: symKey,
Payload: payload,
Topic: topic,
TTL: uint32(*argTTL),
PoW: *argPoW,
WorkTime: uint32(*argWorkTime),
}
msg, err := whisper.NewSentMessage(&params)
if err != nil {
utils.Fatalf("failed to create new message: %s", err)
}
envelope, err := msg.Wrap(&params)
if err != nil {
fmt.Printf("failed to seal message: %v \n", err)
return common.Hash{}
}
err = shh.Send(envelope)
if err != nil {
fmt.Printf("failed to send message: %v \n", err)
return common.Hash{}
}
return envelope.Hash()
}
func messageLoop() {
sf := shh.GetFilter(symFilterID)
if sf == nil {
utils.Fatalf("symmetric filter is not installed")
}
af := shh.GetFilter(asymFilterID)
if af == nil {
utils.Fatalf("asymmetric filter is not installed")
}
ticker := time.NewTicker(time.Millisecond * 50)
defer ticker.Stop()
for {
select {
case <-ticker.C:
m1 := sf.Retrieve()
m2 := af.Retrieve()
messages := append(m1, m2...)
for _, msg := range messages {
reportedOnce := false
if !*fileExMode && len(msg.Payload) <= 2048 {
printMessageInfo(msg)
reportedOnce = true
}
// All messages are saved upon specifying argSaveDir.
// fileExMode only specifies how messages are displayed on the console after they are saved.
// if fileExMode == true, only the hashes are displayed, since messages might be too big.
if len(*argSaveDir) > 0 {
writeMessageToFile(*argSaveDir, msg, !reportedOnce)
}
}
case <-done:
return
}
}
}
func printMessageInfo(msg *whisper.ReceivedMessage) {
timestamp := fmt.Sprintf("%d", msg.Sent) // unix timestamp for diagnostics
text := string(msg.Payload)
var address common.Address
if msg.Src != nil {
address = crypto.PubkeyToAddress(*msg.Src)
}
if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
fmt.Printf("\n%s <%x>: %s\n", timestamp, address, text) // message from myself
} else {
fmt.Printf("\n%s [%x]: %s\n", timestamp, address, text) // message from a peer
}
}
func writeMessageToFile(dir string, msg *whisper.ReceivedMessage, show bool) {
if len(dir) == 0 {
return
}
timestamp := fmt.Sprintf("%d", msg.Sent)
name := fmt.Sprintf("%x", msg.EnvelopeHash)
var address common.Address
if msg.Src != nil {
address = crypto.PubkeyToAddress(*msg.Src)
}
env := shh.GetEnvelope(msg.EnvelopeHash)
if env == nil {
fmt.Printf("\nUnexpected error: envelope not found: %x\n", msg.EnvelopeHash)
return
}
// this is a sample code; uncomment if you don't want to save your own messages.
//if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
// fmt.Printf("\n%s <%x>: message from myself received, not saved: '%s'\n", timestamp, address, name)
// return
//}
fullpath := filepath.Join(dir, name)
err := ioutil.WriteFile(fullpath, env.Data, 0644)
if err != nil {
fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
} else if show {
fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(env.Data))
}
}
func requestExpiredMessagesLoop() {
var key, peerID, bloom []byte
var timeLow, timeUpp uint32
var t string
var xt whisper.TopicType
keyID, err := shh.AddSymKeyFromPassword(msPassword)
if err != nil {
utils.Fatalf("Failed to create symmetric key for mail request: %s", err)
}
key, err = shh.GetSymKey(keyID)
if err != nil {
utils.Fatalf("Failed to save symmetric key for mail request: %s", err)
}
peerID = extractIDFromEnode(*argEnode)
shh.AllowP2PMessagesFromPeer(peerID)
for {
timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ")
t = scanLine("Enter the topic (hex). Press enter to request all messages, regardless of the topic: ")
if len(t) == whisper.TopicLength*2 {
x, err := hex.DecodeString(t)
if err != nil {
fmt.Printf("Failed to parse the topic: %s \n", err)
continue
}
xt = whisper.BytesToTopic(x)
bloom = whisper.TopicToBloom(xt)
obfuscateBloom(bloom)
} else if len(t) == 0 {
bloom = whisper.MakeFullNodeBloom()
} else {
fmt.Println("Error: topic is invalid, request aborted")
continue
}
if timeUpp == 0 {
timeUpp = 0xFFFFFFFF
}
data := make([]byte, 8, 8+whisper.BloomFilterSize)
binary.BigEndian.PutUint32(data, timeLow)
binary.BigEndian.PutUint32(data[4:], timeUpp)
data = append(data, bloom...)
var params whisper.MessageParams
params.PoW = *argServerPoW
params.Payload = data
params.KeySym = key
params.Src = asymKey
params.WorkTime = 5
msg, err := whisper.NewSentMessage(&params)
if err != nil {
utils.Fatalf("failed to create new message: %s", err)
}
env, err := msg.Wrap(&params)
if err != nil {
utils.Fatalf("Wrap failed: %s", err)
}
err = shh.RequestHistoricMessages(peerID, env)
if err != nil {
utils.Fatalf("Failed to send P2P message: %s", err)
}
time.Sleep(time.Second * 5)
}
}
func extractIDFromEnode(s string) []byte {
n, err := enode.Parse(enode.ValidSchemes, s)
if err != nil {
utils.Fatalf("Failed to parse node: %s", err)
}
return n.ID().Bytes()
}
// obfuscateBloom adds 16 random bits to the bloom
// filter, in order to obfuscate the containing topics.
// it does so deterministically within every session.
// despite additional bits, it will match on average
// 32000 times less messages than full node's bloom filter.
func obfuscateBloom(bloom []byte) {
const half = entropySize / 2
for i := 0; i < half; i++ {
x := int(entropy[i])
if entropy[half+i] < 128 {
x += 256
}
bloom[x/8] = 1 << uint(x%8) // set the bit number X
}
}

View File

@@ -28,7 +28,7 @@ import (
// API is a user facing RPC API to allow controlling the signer and voting
// mechanisms of the proof-of-authority scheme.
type API struct {
chain consensus.ChainReader
chain consensus.ChainHeaderReader
clique *Clique
}

View File

@@ -39,6 +39,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru"
"golang.org/x/crypto/sha3"
)
@@ -137,9 +138,8 @@ var (
errRecentlySigned = errors.New("recently signed")
)
// SignerFn is a signer callback function to request a header to be signed by a
// backing account.
type SignerFn func(accounts.Account, string, []byte) ([]byte, error)
// SignerFn hashes and signs the data to be signed by a backing account.
type SignerFn func(signer accounts.Account, mimeType string, message []byte) ([]byte, error)
// ecrecover extracts the Ethereum account address from a signed header.
func ecrecover(header *types.Header, sigcache *lru.ARCCache) (common.Address, error) {
@@ -213,14 +213,14 @@ func (c *Clique) Author(header *types.Header) (common.Address, error) {
}
// VerifyHeader checks whether a header conforms to the consensus rules.
func (c *Clique) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
func (c *Clique) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
return c.verifyHeader(chain, header, nil)
}
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers. The
// method returns a quit channel to abort the operations and a results channel to
// retrieve the async verifications (the order is that of the input slice).
func (c *Clique) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
func (c *Clique) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
abort := make(chan struct{})
results := make(chan error, len(headers))
@@ -242,7 +242,7 @@ func (c *Clique) VerifyHeaders(chain consensus.ChainReader, headers []*types.Hea
// caller may optionally pass in a batch of parents (ascending order) to avoid
// looking those up from the database. This is useful for concurrently verifying
// a batch of new headers.
func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
func (c *Clique) verifyHeader(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
if header.Number == nil {
return errUnknownBlock
}
@@ -305,7 +305,7 @@ func (c *Clique) verifyHeader(chain consensus.ChainReader, header *types.Header,
// rather depend on a batch of previous headers. The caller may optionally pass
// in a batch of parents (ascending order) to avoid looking those up from the
// database. This is useful for concurrently verifying a batch of new headers.
func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
// The genesis block is the always valid dead-end
number := header.Number.Uint64()
if number == 0 {
@@ -345,7 +345,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainReader, header *type
}
// snapshot retrieves the authorization snapshot at a given point in time.
func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
func (c *Clique) snapshot(chain consensus.ChainHeaderReader, number uint64, hash common.Hash, parents []*types.Header) (*Snapshot, error) {
// Search for a snapshot in memory or on disk for checkpoints
var (
headers []*types.Header
@@ -369,7 +369,7 @@ func (c *Clique) snapshot(chain consensus.ChainReader, number uint64, hash commo
// at a checkpoint block without a parent (light client CHT), or we have piled
// up more headers than allowed to be reorged (chain reinit from a freezer),
// consider the checkpoint trusted and snapshot it.
if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > params.ImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) {
if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > params.FullImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) {
checkpoint := chain.GetHeaderByNumber(number)
if checkpoint != nil {
hash := checkpoint.Hash()
@@ -436,7 +436,7 @@ func (c *Clique) VerifyUncles(chain consensus.ChainReader, block *types.Block) e
// VerifySeal implements consensus.Engine, checking whether the signature contained
// in the header satisfies the consensus protocol requirements.
func (c *Clique) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
func (c *Clique) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
return c.verifySeal(chain, header, nil)
}
@@ -444,7 +444,7 @@ func (c *Clique) VerifySeal(chain consensus.ChainReader, header *types.Header) e
// consensus protocol requirements. The method accepts an optional list of parent
// headers that aren't yet part of the local blockchain to generate the snapshots
// from.
func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, parents []*types.Header) error {
func (c *Clique) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
// Verifying the genesis block is not supported
number := header.Number.Uint64()
if number == 0 {
@@ -487,7 +487,7 @@ func (c *Clique) verifySeal(chain consensus.ChainReader, header *types.Header, p
// Prepare implements consensus.Engine, preparing all the consensus fields of the
// header for running the transactions on top.
func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) error {
func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
// If the block isn't a checkpoint, cast a random vote (good enough for now)
header.Coinbase = common.Address{}
header.Nonce = types.BlockNonce{}
@@ -552,7 +552,7 @@ func (c *Clique) Prepare(chain consensus.ChainReader, header *types.Header) erro
// Finalize implements consensus.Engine, ensuring no uncles are set, nor block
// rewards given.
func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
func (c *Clique) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil)
@@ -560,13 +560,13 @@ func (c *Clique) Finalize(chain consensus.ChainReader, header *types.Header, sta
// FinalizeAndAssemble implements consensus.Engine, ensuring no uncles are set,
// nor block rewards given, and returns the final block.
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
func (c *Clique) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// No block rewards in PoA, so the state remains as is and uncles are dropped
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
header.UncleHash = types.CalcUncleHash(nil)
// Assemble and return the final block for sealing
return types.NewBlock(header, txs, nil, receipts), nil
return types.NewBlock(header, txs, nil, receipts, new(trie.Trie)), nil
}
// Authorize injects a private key into the consensus engine to mint new blocks
@@ -581,7 +581,7 @@ func (c *Clique) Authorize(signer common.Address, signFn SignerFn) {
// Seal implements consensus.Engine, attempting to create a sealed block using
// the local signing credentials.
func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
func (c *Clique) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
header := block.Header()
// Sealing the genesis block is not supported
@@ -654,7 +654,7 @@ func (c *Clique) Seal(chain consensus.ChainReader, block *types.Block, results c
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have based on the previous blocks in the chain and the
// current signer.
func (c *Clique) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
func (c *Clique) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
snap, err := c.snapshot(chain, parent.Number.Uint64(), parent.Hash(), nil)
if err != nil {
return nil
@@ -684,7 +684,7 @@ func (c *Clique) Close() error {
// APIs implements consensus.Engine, returning the user facing RPC API to allow
// controlling the signer voting.
func (c *Clique) APIs(chain consensus.ChainReader) []rpc.API {
func (c *Clique) APIs(chain consensus.ChainHeaderReader) []rpc.API {
return []rpc.API{{
Namespace: "clique",
Version: "1.0",

View File

@@ -27,9 +27,9 @@ import (
"github.com/ethereum/go-ethereum/rpc"
)
// ChainReader defines a small collection of methods needed to access the local
// blockchain during header and/or uncle verification.
type ChainReader interface {
// ChainHeaderReader defines a small collection of methods needed to access the local
// blockchain during header verification.
type ChainHeaderReader interface {
// Config retrieves the blockchain's chain configuration.
Config() *params.ChainConfig
@@ -44,6 +44,12 @@ type ChainReader interface {
// GetHeaderByHash retrieves a block header from the database by its hash.
GetHeaderByHash(hash common.Hash) *types.Header
}
// ChainReader defines a small collection of methods needed to access the local
// blockchain during header and/or uncle verification.
type ChainReader interface {
ChainHeaderReader
// GetBlock retrieves a block from the database by hash and number.
GetBlock(hash common.Hash, number uint64) *types.Block
@@ -59,13 +65,13 @@ type Engine interface {
// VerifyHeader checks whether a header conforms to the consensus rules of a
// given engine. Verifying the seal may be done optionally here, or explicitly
// via the VerifySeal method.
VerifyHeader(chain ChainReader, header *types.Header, seal bool) error
VerifyHeader(chain ChainHeaderReader, header *types.Header, seal bool) error
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications (the order is that of
// the input slice).
VerifyHeaders(chain ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
VerifyHeaders(chain ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error)
// VerifyUncles verifies that the given block's uncles conform to the consensus
// rules of a given engine.
@@ -73,18 +79,18 @@ type Engine interface {
// VerifySeal checks whether the crypto seal on a header is valid according to
// the consensus rules of the given engine.
VerifySeal(chain ChainReader, header *types.Header) error
VerifySeal(chain ChainHeaderReader, header *types.Header) error
// Prepare initializes the consensus fields of a block header according to the
// rules of a particular engine. The changes are executed inline.
Prepare(chain ChainReader, header *types.Header) error
Prepare(chain ChainHeaderReader, header *types.Header) error
// Finalize runs any post-transaction state modifications (e.g. block rewards)
// but does not assemble the block.
//
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
Finalize(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
Finalize(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header)
// FinalizeAndAssemble runs any post-transaction state modifications (e.g. block
@@ -92,7 +98,7 @@ type Engine interface {
//
// Note: The block header and state database might be updated to reflect any
// consensus rules that happen at finalization (e.g. block rewards).
FinalizeAndAssemble(chain ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
FinalizeAndAssemble(chain ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error)
// Seal generates a new sealing request for the given input block and pushes
@@ -100,17 +106,17 @@ type Engine interface {
//
// Note, the method returns immediately and will send the result async. More
// than one result may also be returned depending on the consensus algorithm.
Seal(chain ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
Seal(chain ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error
// SealHash returns the hash of a block prior to it being sealed.
SealHash(header *types.Header) common.Hash
// CalcDifficulty is the difficulty adjustment algorithm. It returns the difficulty
// that a new block should have.
CalcDifficulty(chain ChainReader, time uint64, parent *types.Header) *big.Int
CalcDifficulty(chain ChainHeaderReader, time uint64, parent *types.Header) *big.Int
// APIs returns the RPC APIs this consensus engine provides.
APIs(chain ChainReader) []rpc.API
APIs(chain ChainHeaderReader) []rpc.API
// Close terminates any background threads maintained by the consensus engine.
Close() error

View File

@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
"golang.org/x/crypto/sha3"
)
@@ -86,7 +87,7 @@ func (ethash *Ethash) Author(header *types.Header) (common.Address, error) {
// VerifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.Header, seal bool) error {
func (ethash *Ethash) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header, seal bool) error {
// If we're running a full engine faking, accept any input as valid
if ethash.config.PowMode == ModeFullFake {
return nil
@@ -107,7 +108,7 @@ func (ethash *Ethash) VerifyHeader(chain consensus.ChainReader, header *types.He
// VerifyHeaders is similar to VerifyHeader, but verifies a batch of headers
// concurrently. The method returns a quit channel to abort the operations and
// a results channel to retrieve the async verifications.
func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
func (ethash *Ethash) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool) (chan<- struct{}, <-chan error) {
// If we're running a full engine faking, accept any input as valid
if ethash.config.PowMode == ModeFullFake || len(headers) == 0 {
abort, results := make(chan struct{}), make(chan error, len(headers))
@@ -169,7 +170,7 @@ func (ethash *Ethash) VerifyHeaders(chain consensus.ChainReader, headers []*type
return abort, errorsOut
}
func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainReader, headers []*types.Header, seals []bool, index int) error {
func (ethash *Ethash) verifyHeaderWorker(chain consensus.ChainHeaderReader, headers []*types.Header, seals []bool, index int) error {
var parent *types.Header
if index == 0 {
parent = chain.GetHeader(headers[0].ParentHash, headers[0].Number.Uint64()-1)
@@ -243,7 +244,7 @@ func (ethash *Ethash) VerifyUncles(chain consensus.ChainReader, block *types.Blo
// verifyHeader checks whether a header conforms to the consensus rules of the
// stock Ethereum ethash engine.
// See YP section 4.3.4. "Block Header Validity"
func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *types.Header, uncle bool, seal bool) error {
func (ethash *Ethash) verifyHeader(chain consensus.ChainHeaderReader, header, parent *types.Header, uncle bool, seal bool) error {
// Ensure that the header's extra-data section is of a reasonable size
if uint64(len(header.Extra)) > params.MaximumExtraDataSize {
return fmt.Errorf("extra-data too long: %d > %d", len(header.Extra), params.MaximumExtraDataSize)
@@ -306,7 +307,7 @@ func (ethash *Ethash) verifyHeader(chain consensus.ChainReader, header, parent *
// CalcDifficulty is the difficulty adjustment algorithm. It returns
// the difficulty that a new block should have when created at time
// given the parent block's time and difficulty.
func (ethash *Ethash) CalcDifficulty(chain consensus.ChainReader, time uint64, parent *types.Header) *big.Int {
func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
return CalcDifficulty(chain.Config(), time, parent)
}
@@ -486,14 +487,14 @@ func calcDifficultyFrontier(time uint64, parent *types.Header) *big.Int {
// VerifySeal implements consensus.Engine, checking whether the given block satisfies
// the PoW difficulty requirements.
func (ethash *Ethash) VerifySeal(chain consensus.ChainReader, header *types.Header) error {
func (ethash *Ethash) VerifySeal(chain consensus.ChainHeaderReader, header *types.Header) error {
return ethash.verifySeal(chain, header, false)
}
// verifySeal checks whether a block satisfies the PoW difficulty requirements,
// either using the usual ethash cache for it, or alternatively using a full DAG
// to make remote mining fast.
func (ethash *Ethash) verifySeal(chain consensus.ChainReader, header *types.Header, fulldag bool) error {
func (ethash *Ethash) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, fulldag bool) error {
// If we're running a fake PoW, accept any seal as valid
if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
time.Sleep(ethash.fakeDelay)
@@ -558,7 +559,7 @@ func (ethash *Ethash) verifySeal(chain consensus.ChainReader, header *types.Head
// Prepare implements consensus.Engine, initializing the difficulty field of a
// header to conform to the ethash protocol. The changes are done inline.
func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header) error {
func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
parent := chain.GetHeader(header.ParentHash, header.Number.Uint64()-1)
if parent == nil {
return consensus.ErrUnknownAncestor
@@ -569,7 +570,7 @@ func (ethash *Ethash) Prepare(chain consensus.ChainReader, header *types.Header)
// Finalize implements consensus.Engine, accumulating the block and uncle rewards,
// setting the final state on the header
func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
func (ethash *Ethash) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header) {
// Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
@@ -577,13 +578,13 @@ func (ethash *Ethash) Finalize(chain consensus.ChainReader, header *types.Header
// FinalizeAndAssemble implements consensus.Engine, accumulating the block and
// uncle rewards, setting the final state and assembling the block.
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
func (ethash *Ethash) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt) (*types.Block, error) {
// Accumulate any block and uncle rewards and commit the final state root
accumulateRewards(chain.Config(), state, header, uncles)
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
// Header seems complete, assemble into a block and return
return types.NewBlock(header, txs, uncles, receipts), nil
return types.NewBlock(header, txs, uncles, receipts, new(trie.Trie)), nil
}
// SealHash returns the hash of a block prior to it being sealed.

View File

@@ -656,7 +656,7 @@ func (ethash *Ethash) Hashrate() float64 {
}
// APIs implements consensus.Engine, returning the user facing RPC APIs.
func (ethash *Ethash) APIs(chain consensus.ChainReader) []rpc.API {
func (ethash *Ethash) APIs(chain consensus.ChainHeaderReader) []rpc.API {
// In order to ensure backward compatibility, we exposes ethash RPC APIs
// to both eth and ethash namespaces.
return []rpc.API{

View File

@@ -48,7 +48,7 @@ var (
// Seal implements consensus.Engine, attempting to find a nonce that satisfies
// the block's difficulty requirements.
func (ethash *Ethash) Seal(chain consensus.ChainReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
func (ethash *Ethash) Seal(chain consensus.ChainHeaderReader, block *types.Block, results chan<- *types.Block, stop <-chan struct{}) error {
// If we're running a fake PoW, simply return a 0 nonce immediately
if ethash.config.PowMode == ModeFake || ethash.config.PowMode == ModeFullFake {
header := block.Header()

View File

@@ -306,9 +306,9 @@ func (b *bridge) Sign(call jsre.Call) (goja.Value, error) {
}
// Send the request to the backend and return
sign, callable := goja.AssertFunction(getJeth(call.VM).Get("unlockAccount"))
sign, callable := goja.AssertFunction(getJeth(call.VM).Get("sign"))
if !callable {
return nil, fmt.Errorf("jeth.unlockAccount is not callable")
return nil, fmt.Errorf("jeth.sign is not callable")
}
return sign(goja.Null(), message, account, passwd)
}

View File

@@ -109,7 +109,8 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
if confOverride != nil {
confOverride(ethConf)
}
if err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil {
ethBackend, err := eth.New(stack, ethConf)
if err != nil {
t.Fatalf("failed to register Ethereum protocol: %v", err)
}
// Start the node and assemble the JavaScript console around it
@@ -135,13 +136,10 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
t.Fatalf("failed to create JavaScript console: %v", err)
}
// Create the final tester and return
var ethereum *eth.Ethereum
stack.Service(&ethereum)
return &tester{
workspace: workspace,
stack: stack,
ethereum: ethereum,
ethereum: ethBackend,
console: console,
input: prompter,
output: printer,

View File

@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
)
// BlockValidator is responsible for validating block headers, uncles and
@@ -61,7 +62,7 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
return fmt.Errorf("uncle root hash mismatch: have %x, want %x", hash, header.UncleHash)
}
if hash := types.DeriveSha(block.Transactions()); hash != header.TxHash {
if hash := types.DeriveSha(block.Transactions(), new(trie.Trie)); hash != header.TxHash {
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
}
if !v.bc.HasBlockAndState(block.ParentHash(), block.NumberU64()-1) {
@@ -89,7 +90,7 @@ func (v *BlockValidator) ValidateState(block *types.Block, statedb *state.StateD
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
}
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
receiptSha := types.DeriveSha(receipts)
receiptSha := types.DeriveSha(receipts, new(trie.Trie))
if receiptSha != header.ReceiptHash {
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
}

View File

@@ -70,8 +70,11 @@ var (
blockValidationTimer = metrics.NewRegisteredTimer("chain/validation", nil)
blockExecutionTimer = metrics.NewRegisteredTimer("chain/execution", nil)
blockWriteTimer = metrics.NewRegisteredTimer("chain/write", nil)
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
blockReorgMeter = metrics.NewRegisteredMeter("chain/reorg/executes", nil)
blockReorgAddMeter = metrics.NewRegisteredMeter("chain/reorg/add", nil)
blockReorgDropMeter = metrics.NewRegisteredMeter("chain/reorg/drop", nil)
blockReorgInvalidatedTx = metrics.NewRegisteredMeter("chain/reorg/invalidTx", nil)
blockPrefetchExecuteTimer = metrics.NewRegisteredTimer("chain/prefetch/executes", nil)
blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)
@@ -109,13 +112,18 @@ const (
// - Version 7
// The following incompatible database changes were added:
// * Use freezer as the ancient database to maintain all ancient data
BlockChainVersion uint64 = 7
// - Version 8
// The following incompatible database changes were added:
// * New scheme for contract code in order to separate the codes and trie nodes
BlockChainVersion uint64 = 8
)
// CacheConfig contains the configuration values for the trie caching/pruning
// that's resident in a blockchain.
type CacheConfig struct {
TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory
TrieCleanJournal string // Disk journal for saving clean cache entries.
TrieCleanRejournal time.Duration // Time interval to dump clean cache to disk periodically
TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks
TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk
TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node)
@@ -125,6 +133,16 @@ type CacheConfig struct {
SnapshotWait bool // Wait for snapshot construction on startup. TODO(karalabe): This is a dirty hack for testing, nuke it
}
// defaultCacheConfig are the default caching values if none are specified by the
// user (also used during testing).
var defaultCacheConfig = &CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256,
SnapshotWait: true,
}
// BlockChain represents the canonical chain given a database with a genesis
// block. The Blockchain manages chain imports, reverts, chain reorganisations.
//
@@ -199,13 +217,7 @@ type BlockChain struct {
// Processor.
func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *params.ChainConfig, engine consensus.Engine, vmConfig vm.Config, shouldPreserve func(block *types.Block) bool, txLookupLimit *uint64) (*BlockChain, error) {
if cacheConfig == nil {
cacheConfig = &CacheConfig{
TrieCleanLimit: 256,
TrieDirtyLimit: 256,
TrieTimeLimit: 5 * time.Minute,
SnapshotLimit: 256,
SnapshotWait: true,
}
cacheConfig = defaultCacheConfig
}
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
@@ -220,7 +232,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
cacheConfig: cacheConfig,
db: db,
triegc: prque.New(nil),
stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit),
stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit, cacheConfig.TrieCleanJournal),
quit: make(chan struct{}),
shouldPreserve: shouldPreserve,
bodyCache: bodyCache,
@@ -263,15 +275,18 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
txIndexBlock = frozen
}
}
if err := bc.loadLastState(); err != nil {
return nil, err
}
// The first thing the node will do is reconstruct the verification data for
// the head block (ethash cache or clique voting snapshot). Might as well do
// it in advance.
bc.engine.VerifyHeader(bc, bc.CurrentHeader(), true)
// Make sure the state associated with the block is available
head := bc.CurrentBlock()
if _, err := state.New(head.Root(), bc.stateCache, bc.snaps); err != nil {
log.Warn("Head state missing, repairing", "number", head.Number(), "hash", head.Hash())
if err := bc.SetHead(head.NumberU64()); err != nil {
return nil, err
}
}
// Ensure that a previous crash in SetHead doesn't leave extra ancients
if frozen, err := bc.db.Ancients(); err == nil && frozen > 0 {
var (
needRewind bool
@@ -281,7 +296,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
// blockchain repair. If the head full block is even lower than the ancient
// chain, truncate the ancient store.
fullBlock := bc.CurrentBlock()
if fullBlock != nil && fullBlock != bc.genesisBlock && fullBlock.NumberU64() < frozen-1 {
if fullBlock != nil && fullBlock.Hash() != bc.genesisBlock.Hash() && fullBlock.NumberU64() < frozen-1 {
needRewind = true
low = fullBlock.NumberU64()
}
@@ -296,15 +311,17 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
}
}
if needRewind {
var hashes []common.Hash
previous := bc.CurrentHeader().Number.Uint64()
for i := low + 1; i <= bc.CurrentHeader().Number.Uint64(); i++ {
hashes = append(hashes, rawdb.ReadCanonicalHash(bc.db, i))
log.Error("Truncating ancient chain", "from", bc.CurrentHeader().Number.Uint64(), "to", low)
if err := bc.SetHead(low); err != nil {
return nil, err
}
bc.Rollback(hashes)
log.Warn("Truncate ancient chain", "from", previous, "to", low)
}
}
// The first thing the node will do is reconstruct the verification data for
// the head block (ethash cache or clique voting snapshot). Might as well do
// it in advance.
bc.engine.VerifyHeader(bc, bc.CurrentHeader(), true)
// Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
for hash := range BadHashes {
if header := bc.GetHeaderByHash(hash); header != nil {
@@ -313,7 +330,9 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
// make sure the headerByNumber (if present) is in our current canonical chain
if headerByNumber != nil && headerByNumber.Hash() == header.Hash() {
log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash)
bc.SetHead(header.Number.Uint64() - 1)
if err := bc.SetHead(header.Number.Uint64() - 1); err != nil {
return nil, err
}
log.Error("Chain rewind was successful, resuming normal operation")
}
}
@@ -328,6 +347,19 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
bc.txLookupLimit = *txLookupLimit
go bc.maintainTxIndex(txIndexBlock)
}
// If periodic cache journal is required, spin it up.
if bc.cacheConfig.TrieCleanRejournal > 0 {
if bc.cacheConfig.TrieCleanRejournal < time.Minute {
log.Warn("Sanitizing invalid trie cache journal time", "provided", bc.cacheConfig.TrieCleanRejournal, "updated", time.Minute)
bc.cacheConfig.TrieCleanRejournal = time.Minute
}
triedb := bc.stateCache.TrieDB()
bc.wg.Add(1)
go func() {
defer bc.wg.Done()
triedb.SaveCachePeriodically(bc.cacheConfig.TrieCleanJournal, bc.cacheConfig.TrieCleanRejournal, bc.quit)
}()
}
return bc, nil
}
@@ -367,15 +399,6 @@ func (bc *BlockChain) loadLastState() error {
log.Warn("Head block missing, resetting chain", "hash", head)
return bc.Reset()
}
// Make sure the state associated with the block is available
if _, err := state.New(currentBlock.Root(), bc.stateCache, bc.snaps); err != nil {
// Dangling block without a state associated, init from scratch
log.Warn("Head state missing, repairing chain", "number", currentBlock.Number(), "hash", currentBlock.Hash())
if err := bc.repair(&currentBlock); err != nil {
return err
}
rawdb.WriteHeadBlockHash(bc.db, currentBlock.Hash())
}
// Everything seems to be fine, set as the head block
bc.currentBlock.Store(currentBlock)
headBlockGauge.Update(int64(currentBlock.NumberU64()))
@@ -409,30 +432,48 @@ func (bc *BlockChain) loadLastState() error {
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd, "age", common.PrettyAge(time.Unix(int64(currentHeader.Time), 0)))
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd, "age", common.PrettyAge(time.Unix(int64(currentBlock.Time()), 0)))
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd, "age", common.PrettyAge(time.Unix(int64(currentFastBlock.Time()), 0)))
if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil {
log.Info("Loaded last fast-sync pivot marker", "number", *pivot)
}
return nil
}
// SetHead rewinds the local chain to a new head. In the case of headers, everything
// above the new head will be deleted and the new one set. In the case of blocks
// though, the head may be further rewound if block bodies are missing (non-archive
// nodes after a fast sync).
// SetHead rewinds the local chain to a new head. Depending on whether the node
// was fast synced or full synced and in which state, the method will try to
// delete minimal data from disk whilst retaining chain consistency.
func (bc *BlockChain) SetHead(head uint64) error {
log.Warn("Rewinding blockchain", "target", head)
bc.chainmu.Lock()
defer bc.chainmu.Unlock()
updateFn := func(db ethdb.KeyValueWriter, header *types.Header) {
// Rewind the block chain, ensuring we don't end up with a stateless head block
if currentBlock := bc.CurrentBlock(); currentBlock != nil && header.Number.Uint64() < currentBlock.NumberU64() {
// Retrieve the last pivot block to short circuit rollbacks beyond it and the
// current freezer limit to start nuking id underflown
pivot := rawdb.ReadLastPivotNumber(bc.db)
frozen, _ := bc.db.Ancients()
updateFn := func(db ethdb.KeyValueWriter, header *types.Header) (uint64, bool) {
// Rewind the block chain, ensuring we don't end up with a stateless head
// block. Note, depth equality is permitted to allow using SetHead as a
// chain reparation mechanism without deleting any data!
if currentBlock := bc.CurrentBlock(); currentBlock != nil && header.Number.Uint64() <= currentBlock.NumberU64() {
newHeadBlock := bc.GetBlock(header.Hash(), header.Number.Uint64())
if newHeadBlock == nil {
log.Error("Gap in the chain, rewinding to genesis", "number", header.Number, "hash", header.Hash())
newHeadBlock = bc.genesisBlock
} else {
if _, err := state.New(newHeadBlock.Root(), bc.stateCache, bc.snaps); err != nil {
// Rewound state missing, rolled back to before pivot, reset to genesis
newHeadBlock = bc.genesisBlock
// Block exists, keep rewinding until we find one with state
for {
if _, err := state.New(newHeadBlock.Root(), bc.stateCache, bc.snaps); err != nil {
log.Trace("Block state missing, rewinding further", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
if pivot == nil || newHeadBlock.NumberU64() > *pivot {
newHeadBlock = bc.GetBlock(newHeadBlock.ParentHash(), newHeadBlock.NumberU64()-1)
continue
} else {
log.Trace("Rewind passed pivot, aiming genesis", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash(), "pivot", *pivot)
newHeadBlock = bc.genesisBlock
}
}
log.Debug("Rewound to block with state", "number", newHeadBlock.NumberU64(), "hash", newHeadBlock.Hash())
break
}
}
rawdb.WriteHeadBlockHash(db, newHeadBlock.Hash())
@@ -444,7 +485,6 @@ func (bc *BlockChain) SetHead(head uint64) error {
bc.currentBlock.Store(newHeadBlock)
headBlockGauge.Update(int64(newHeadBlock.NumberU64()))
}
// Rewind the fast block in a simpleton way to the target head
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && header.Number.Uint64() < currentFastBlock.NumberU64() {
newHeadFastBlock := bc.GetBlock(header.Hash(), header.Number.Uint64())
@@ -461,8 +501,17 @@ func (bc *BlockChain) SetHead(head uint64) error {
bc.currentFastBlock.Store(newHeadFastBlock)
headFastBlockGauge.Update(int64(newHeadFastBlock.NumberU64()))
}
}
head := bc.CurrentBlock().NumberU64()
// If setHead underflown the freezer threshold and the block processing
// intent afterwards is full block importing, delete the chain segment
// between the stateful-block and the sethead target.
var wipe bool
if head+1 < frozen {
wipe = pivot == nil || head >= *pivot
}
return head, wipe // Only force wipe if full synced
}
// Rewind the header chain, deleting all block bodies until then
delFn := func(db ethdb.KeyValueWriter, hash common.Hash, num uint64) {
// Ignore the error here since light client won't hit this path
@@ -470,10 +519,9 @@ func (bc *BlockChain) SetHead(head uint64) error {
if num+1 <= frozen {
// Truncate all relative data(header, total difficulty, body, receipt
// and canonical hash) from ancient store.
if err := bc.db.TruncateAncients(num + 1); err != nil {
if err := bc.db.TruncateAncients(num); err != nil {
log.Crit("Failed to truncate ancient data", "number", num, "err", err)
}
// Remove the hash <-> number mapping from the active store.
rawdb.DeleteHeaderNumber(db, hash)
} else {
@@ -485,8 +533,18 @@ func (bc *BlockChain) SetHead(head uint64) error {
}
// Todo(rjl493456442) txlookup, bloombits, etc
}
bc.hc.SetHead(head, updateFn, delFn)
// If SetHead was only called as a chain reparation method, try to skip
// touching the header chain altogether, unless the freezer is broken
if block := bc.CurrentBlock(); block.NumberU64() == head {
if target, force := updateFn(bc.db, block.Header()); force {
bc.hc.SetHead(target, updateFn, delFn)
}
} else {
// Rewind the chain to the requested head and keep going backwards until a
// block with a state is found or fast sync pivot is passed
log.Warn("Rewinding blockchain", "target", head)
bc.hc.SetHead(head, updateFn, delFn)
}
// Clear out any stale content from the caches
bc.bodyCache.Purge()
bc.bodyRLPCache.Purge()
@@ -609,28 +667,6 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
return nil
}
// repair tries to repair the current blockchain by rolling back the current block
// until one with associated state is found. This is needed to fix incomplete db
// writes caused either by crashes/power outages, or simply non-committed tries.
//
// This method only rolls back the current block. The current header and current
// fast block are left intact.
func (bc *BlockChain) repair(head **types.Block) error {
for {
// Abort if we've rewound to a head block that does have associated state
if _, err := state.New((*head).Root(), bc.stateCache, bc.snaps); err == nil {
log.Info("Rewound blockchain to past state", "number", (*head).Number(), "hash", (*head).Hash())
return nil
}
// Otherwise rewind one block and recheck state availability there
block := bc.GetBlock((*head).ParentHash(), (*head).NumberU64()-1)
if block == nil {
return fmt.Errorf("missing block %d [%x]", (*head).NumberU64()-1, (*head).ParentHash())
}
*head = block
}
}
// Export writes the active chain to the given writer.
func (bc *BlockChain) Export(w io.Writer) error {
return bc.ExportN(w, uint64(0), bc.CurrentBlock().NumberU64())
@@ -862,12 +898,30 @@ func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.
return uncles
}
// TrieNode retrieves a blob of data associated with a trie node (or code hash)
// TrieNode retrieves a blob of data associated with a trie node
// either from ephemeral in-memory cache, or from persistent storage.
func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
return bc.stateCache.TrieDB().Node(hash)
}
// ContractCode retrieves a blob of data associated with a contract hash
// either from ephemeral in-memory cache, or from persistent storage.
func (bc *BlockChain) ContractCode(hash common.Hash) ([]byte, error) {
return bc.stateCache.ContractCode(common.Hash{}, hash)
}
// ContractCodeWithPrefix retrieves a blob of data associated with a contract
// hash either from ephemeral in-memory cache, or from persistent storage.
//
// If the code doesn't exist in the in-memory cache, check the storage with
// new code scheme.
func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
type codeReader interface {
ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error)
}
return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Hash{}, hash)
}
// Stop stops the blockchain service. If any imports are currently in progress
// it will abort them using the procInterrupt.
func (bc *BlockChain) Stop() {
@@ -901,14 +955,14 @@ func (bc *BlockChain) Stop() {
recent := bc.GetBlockByNumber(number - offset)
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
if err := triedb.Commit(recent.Root(), true); err != nil {
if err := triedb.Commit(recent.Root(), true, nil); err != nil {
log.Error("Failed to commit recent state trie", "err", err)
}
}
}
if snapBase != (common.Hash{}) {
log.Info("Writing snapshot state to disk", "root", snapBase)
if err := triedb.Commit(snapBase, true); err != nil {
if err := triedb.Commit(snapBase, true, nil); err != nil {
log.Error("Failed to commit recent state trie", "err", err)
}
}
@@ -919,6 +973,12 @@ func (bc *BlockChain) Stop() {
log.Error("Dangling trie nodes after full cleanup")
}
}
// Ensure all live cached entries be saved into disk, so that we can skip
// cache warmup when node restarts.
if bc.cacheConfig.TrieCleanJournal != "" {
triedb := bc.stateCache.TrieDB()
triedb.SaveCache(bc.cacheConfig.TrieCleanJournal)
}
log.Info("Blockchain stopped")
}
@@ -961,52 +1021,6 @@ const (
SideStatTy
)
// Rollback is designed to remove a chain of links from the database that aren't
// certain enough to be valid.
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 {
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(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(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
// but the head indicator has been updated in the active store. Regarding this issue,
// system will self recovery by truncating the extra data during the setup phase.
if err := bc.truncateAncient(bc.hc.CurrentHeader().Number.Uint64()); err != nil {
log.Crit("Truncate ancient store failed", "err", err)
}
}
// truncateAncient rewinds the blockchain to the specified header and deletes all
// data in the ancient store that exceeds the specified header.
func (bc *BlockChain) truncateAncient(head uint64) error {
@@ -1263,6 +1277,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
}
// writeLive writes blockchain and corresponding receipt chain into active store.
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
skipPresenceCheck := false
batch := bc.db.NewBatch()
for i, block := range blockChain {
// Short circuit insertion if shutting down or processing failed
@@ -1273,9 +1288,17 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
if !bc.HasHeader(block.Hash(), block.NumberU64()) {
return i, fmt.Errorf("containing header #%d [%x…] unknown", block.Number(), block.Hash().Bytes()[:4])
}
if bc.HasBlock(block.Hash(), block.NumberU64()) {
stats.ignored++
continue
if !skipPresenceCheck {
// Ignore if the entire data is already known
if bc.HasBlock(block.Hash(), block.NumberU64()) {
stats.ignored++
continue
} else {
// If block N is not present, neither are the later blocks.
// This should be true, but if we are mistaken, the shortcut
// here will only cause overwriting of some existing data
skipPresenceCheck = true
}
}
// Write all the data out into the database
rawdb.WriteBody(batch, block.Hash(), block.NumberU64(), block.Body())
@@ -1442,7 +1465,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
// If we're running an archive node, always flush
if bc.cacheConfig.TrieDirtyDisabled {
if err := triedb.Commit(root, false); err != nil {
if err := triedb.Commit(root, false, nil); err != nil {
return NonStatTy, err
}
} else {
@@ -1476,7 +1499,7 @@ func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.
log.Info("State in memory for too long, committing", "time", bc.gcproc, "allowance", bc.cacheConfig.TrieTimeLimit, "optimum", float64(chosen-lastWrite)/TriesInMemory)
}
// Flush an entire trie and restart the counters
triedb.Commit(header.Root, true)
triedb.Commit(header.Root, true, nil)
lastWrite = chosen
bc.gcproc = 0
}
@@ -1685,13 +1708,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
}
switch {
// First block is pruned, insert as sidechain and reorg only if TD grows enough
case err == consensus.ErrPrunedAncestor:
case errors.Is(err, consensus.ErrPrunedAncestor):
log.Debug("Pruned ancestor, inserting as sidechain", "number", block.Number(), "hash", block.Hash())
return bc.insertSideChain(block, it)
// First block is future, shove it (and all children) to the future queue (unknown ancestor)
case err == consensus.ErrFutureBlock || (err == consensus.ErrUnknownAncestor && bc.futureBlocks.Contains(it.first().ParentHash())):
for block != nil && (it.index == 0 || err == consensus.ErrUnknownAncestor) {
case errors.Is(err, consensus.ErrFutureBlock) || (errors.Is(err, consensus.ErrUnknownAncestor) && bc.futureBlocks.Contains(it.first().ParentHash())):
for block != nil && (it.index == 0 || errors.Is(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, err
@@ -1874,13 +1897,13 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
stats.report(chain, it.index, dirty)
}
// Any blocks remaining here? The only ones we care about are the future ones
if block != nil && err == consensus.ErrFutureBlock {
if block != nil && errors.Is(err, consensus.ErrFutureBlock) {
if err := bc.addFutureBlock(block); err != nil {
return it.index, err
}
block, err = it.next()
for ; block != nil && err == consensus.ErrUnknownAncestor; block, err = it.next() {
for ; block != nil && errors.Is(err, consensus.ErrUnknownAncestor); block, err = it.next() {
if err := bc.addFutureBlock(block); err != nil {
return it.index, err
}
@@ -1908,7 +1931,7 @@ func (bc *BlockChain) insertSideChain(block *types.Block, it *insertIterator) (i
// ones. Any other errors means that the block is invalid, and should not be written
// to disk.
err := consensus.ErrPrunedAncestor
for ; block != nil && (err == consensus.ErrPrunedAncestor); block, err = it.next() {
for ; block != nil && errors.Is(err, consensus.ErrPrunedAncestor); block, err = it.next() {
// Check the canonical state root for that number
if number := block.NumberU64(); current.NumberU64() >= number {
canonical := bc.GetBlockByNumber(number)
@@ -2131,6 +2154,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
"drop", len(oldChain), "dropfrom", oldChain[0].Hash(), "add", len(newChain), "addfrom", newChain[0].Hash())
blockReorgAddMeter.Mark(int64(len(newChain)))
blockReorgDropMeter.Mark(int64(len(oldChain)))
blockReorgMeter.Mark(1)
} else {
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
)
// So we can deterministically seed different blockchains
@@ -681,12 +682,12 @@ func TestFastVsFullChains(t *testing.T) {
}
if fblock, arblock, anblock := fast.GetBlockByHash(hash), archive.GetBlockByHash(hash), ancient.GetBlockByHash(hash); fblock.Hash() != arblock.Hash() || anblock.Hash() != arblock.Hash() {
t.Errorf("block #%d [%x]: block mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock, anblock, arblock)
} else if types.DeriveSha(fblock.Transactions()) != types.DeriveSha(arblock.Transactions()) || types.DeriveSha(anblock.Transactions()) != types.DeriveSha(arblock.Transactions()) {
} else if types.DeriveSha(fblock.Transactions(), new(trie.Trie)) != types.DeriveSha(arblock.Transactions(), new(trie.Trie)) || types.DeriveSha(anblock.Transactions(), new(trie.Trie)) != types.DeriveSha(arblock.Transactions(), new(trie.Trie)) {
t.Errorf("block #%d [%x]: transactions mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Transactions(), anblock.Transactions(), arblock.Transactions())
} else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) || types.CalcUncleHash(anblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) {
t.Errorf("block #%d [%x]: uncles mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Uncles(), anblock, arblock.Uncles())
}
if freceipts, anreceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, *rawdb.ReadHeaderNumber(fastDb, hash), fast.Config()), rawdb.ReadReceipts(ancientDb, hash, *rawdb.ReadHeaderNumber(ancientDb, hash), fast.Config()), rawdb.ReadReceipts(archiveDb, hash, *rawdb.ReadHeaderNumber(archiveDb, hash), fast.Config()); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) {
if freceipts, anreceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, *rawdb.ReadHeaderNumber(fastDb, hash), fast.Config()), rawdb.ReadReceipts(ancientDb, hash, *rawdb.ReadHeaderNumber(ancientDb, hash), fast.Config()), rawdb.ReadReceipts(archiveDb, hash, *rawdb.ReadHeaderNumber(archiveDb, hash), fast.Config()); types.DeriveSha(freceipts, new(trie.Trie)) != types.DeriveSha(areceipts, new(trie.Trie)) {
t.Errorf("block #%d [%x]: receipts mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, freceipts, anreceipts, areceipts)
}
}
@@ -731,12 +732,12 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
return db, func() { os.RemoveAll(dir) }
}
// Configure a subchain to roll back
remove := []common.Hash{}
for _, block := range blocks[height/2:] {
remove = append(remove, block.Hash())
}
remove := blocks[height/2].NumberU64()
// Create a small assertion method to check the three heads
assert := func(t *testing.T, kind string, chain *BlockChain, header uint64, fast uint64, block uint64) {
t.Helper()
if num := chain.CurrentBlock().NumberU64(); num != block {
t.Errorf("%s head block mismatch: have #%v, want #%v", kind, num, block)
}
@@ -750,14 +751,18 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
// Import the chain as an archive node and ensure all pointers are updated
archiveDb, delfn := makeDb()
defer delfn()
archive, _ := NewBlockChain(archiveDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
archiveCaching := *defaultCacheConfig
archiveCaching.TrieDirtyDisabled = true
archive, _ := NewBlockChain(archiveDb, &archiveCaching, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
if n, err := archive.InsertChain(blocks); err != nil {
t.Fatalf("failed to process block %d: %v", n, err)
}
defer archive.Stop()
assert(t, "archive", archive, height, height, height)
archive.Rollback(remove)
archive.SetHead(remove - 1)
assert(t, "archive", archive, height/2, height/2, height/2)
// Import the chain as a non-archive node and ensure all pointers are updated
@@ -777,7 +782,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
t.Fatalf("failed to insert receipt %d: %v", n, err)
}
assert(t, "fast", fast, height, height, 0)
fast.Rollback(remove)
fast.SetHead(remove - 1)
assert(t, "fast", fast, height/2, height/2, 0)
// Import the chain as a ancient-first node and ensure all pointers are updated
@@ -793,12 +798,12 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
t.Fatalf("failed to insert receipt %d: %v", n, err)
}
assert(t, "ancient", ancient, height, height, 0)
ancient.Rollback(remove)
assert(t, "ancient", ancient, height/2, height/2, 0)
if frozen, err := ancientDb.Ancients(); err != nil || frozen != height/2+1 {
t.Fatalf("failed to truncate ancient store, want %v, have %v", height/2+1, frozen)
}
ancient.SetHead(remove - 1)
assert(t, "ancient", ancient, 0, 0, 0)
if frozen, err := ancientDb.Ancients(); err != nil || frozen != 1 {
t.Fatalf("failed to truncate ancient store, want %v, have %v", 1, frozen)
}
// Import the chain as a light node and ensure all pointers are updated
lightDb, delfn := makeDb()
defer delfn()
@@ -809,7 +814,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
defer light.Stop()
assert(t, "light", light, height, 0, 0)
light.Rollback(remove)
light.SetHead(remove - 1)
assert(t, "light", light, height/2, 0, 0)
}
@@ -1585,6 +1590,7 @@ func TestBlockchainRecovery(t *testing.T) {
t.Fatalf("failed to create temp freezer dir: %v", err)
}
defer os.Remove(frdir)
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "")
if err != nil {
t.Fatalf("failed to create temp freezer db: %v", err)
@@ -1602,6 +1608,7 @@ func TestBlockchainRecovery(t *testing.T) {
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
t.Fatalf("failed to insert receipt %d: %v", n, err)
}
rawdb.WriteLastPivotNumber(ancientDb, blocks[len(blocks)-1].NumberU64()) // Force fast sync behavior
ancient.Stop()
// Destroy head fast block manually
@@ -1912,11 +1919,9 @@ func testInsertKnownChainData(t *testing.T, typ string) {
asserter(t, blocks[len(blocks)-1])
// Import a long canonical chain with some known data as prefix.
var rollback []common.Hash
for i := len(blocks) / 2; i < len(blocks); i++ {
rollback = append(rollback, blocks[i].Hash())
}
chain.Rollback(rollback)
rollback := blocks[len(blocks)/2].NumberU64()
chain.SetHead(rollback - 1)
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
t.Fatalf("failed to insert chain data: %v", err)
}
@@ -1936,11 +1941,7 @@ func testInsertKnownChainData(t *testing.T, typ string) {
asserter(t, blocks3[len(blocks3)-1])
// Rollback the heavier chain and re-insert the longer chain again
for i := 0; i < len(blocks3); i++ {
rollback = append(rollback, blocks3[i].Hash())
}
chain.Rollback(rollback)
chain.SetHead(rollback - 1)
if err := inserter(append(blocks, blocks2...), append(receipts, receipts2...)); err != nil {
t.Fatalf("failed to insert chain data: %v", err)
}

View File

@@ -155,7 +155,6 @@ func (m *Matcher) Start(ctx context.Context, begin, end uint64, results chan uin
session := &MatcherSession{
matcher: m,
quit: make(chan struct{}),
kill: make(chan struct{}),
ctx: ctx,
}
for _, scheduler := range m.schedulers {
@@ -386,10 +385,8 @@ func (m *Matcher) distributor(dist chan *request, session *MatcherSession) {
requests = make(map[uint][]uint64) // Per-bit list of section requests, ordered by section number
unallocs = make(map[uint]struct{}) // Bits with pending requests but not allocated to any retriever
retrievers chan chan uint // Waiting retrievers (toggled to nil if unallocs is empty)
)
var (
allocs int // Number of active allocations to handle graceful shutdown requests
shutdown = session.quit // Shutdown request channel, will gracefully wait for pending requests
allocs int // Number of active allocations to handle graceful shutdown requests
shutdown = session.quit // Shutdown request channel, will gracefully wait for pending requests
)
// assign is a helper method fo try to assign a pending bit an actively
@@ -409,15 +406,12 @@ func (m *Matcher) distributor(dist chan *request, session *MatcherSession) {
for {
select {
case <-shutdown:
// Graceful shutdown requested, wait until all pending requests are honoured
// Shutdown requested. No more retrievers can be allocated,
// but we still need to wait until all pending requests have returned.
shutdown = nil
if allocs == 0 {
return
}
shutdown = nil
case <-session.kill:
// Pending requests not honoured in time, hard terminate
return
case req := <-dist:
// New retrieval request arrived to be distributed to some fetcher process
@@ -499,8 +493,9 @@ func (m *Matcher) distributor(dist chan *request, session *MatcherSession) {
assign(result.Bit)
}
}
// If we're in the process of shutting down, terminate
if allocs == 0 && shutdown == nil {
// End the session when all pending deliveries have arrived.
if shutdown == nil && allocs == 0 {
return
}
}
@@ -514,7 +509,6 @@ type MatcherSession struct {
closer sync.Once // Sync object to ensure we only ever close once
quit chan struct{} // Quit channel to request pipeline termination
kill chan struct{} // Term channel to signal non-graceful forced shutdown
ctx context.Context // Context used by the light client to abort filtering
err atomic.Value // Global error to track retrieval failures deep in the chain
@@ -529,7 +523,6 @@ func (s *MatcherSession) Close() {
s.closer.Do(func() {
// Signal termination and wait for all goroutines to tear down
close(s.quit)
time.AfterFunc(time.Second, func() { close(s.kill) })
s.pend.Wait()
})
}
@@ -542,10 +535,10 @@ func (s *MatcherSession) Error() error {
return nil
}
// AllocateRetrieval assigns a bloom bit index to a client process that can either
// allocateRetrieval assigns a bloom bit index to a client process that can either
// immediately request and fetch the section contents assigned to this bit or wait
// a little while for more sections to be requested.
func (s *MatcherSession) AllocateRetrieval() (uint, bool) {
func (s *MatcherSession) allocateRetrieval() (uint, bool) {
fetcher := make(chan uint)
select {
@@ -557,9 +550,9 @@ func (s *MatcherSession) AllocateRetrieval() (uint, bool) {
}
}
// PendingSections returns the number of pending section retrievals belonging to
// pendingSections returns the number of pending section retrievals belonging to
// the given bloom bit index.
func (s *MatcherSession) PendingSections(bit uint) int {
func (s *MatcherSession) pendingSections(bit uint) int {
fetcher := make(chan uint)
select {
@@ -571,9 +564,9 @@ func (s *MatcherSession) PendingSections(bit uint) int {
}
}
// AllocateSections assigns all or part of an already allocated bit-task queue
// allocateSections assigns all or part of an already allocated bit-task queue
// to the requesting process.
func (s *MatcherSession) AllocateSections(bit uint, count int) []uint64 {
func (s *MatcherSession) allocateSections(bit uint, count int) []uint64 {
fetcher := make(chan *Retrieval)
select {
@@ -589,14 +582,10 @@ func (s *MatcherSession) AllocateSections(bit uint, count int) []uint64 {
}
}
// DeliverSections delivers a batch of section bit-vectors for a specific bloom
// deliverSections delivers a batch of section bit-vectors for a specific bloom
// bit index to be injected into the processing pipeline.
func (s *MatcherSession) DeliverSections(bit uint, sections []uint64, bitsets [][]byte) {
select {
case <-s.kill:
return
case s.matcher.deliveries <- &Retrieval{Bit: bit, Sections: sections, Bitsets: bitsets}:
}
func (s *MatcherSession) deliverSections(bit uint, sections []uint64, bitsets [][]byte) {
s.matcher.deliveries <- &Retrieval{Bit: bit, Sections: sections, Bitsets: bitsets}
}
// Multiplex polls the matcher session for retrieval tasks and multiplexes it into
@@ -608,17 +597,17 @@ func (s *MatcherSession) DeliverSections(bit uint, sections []uint64, bitsets []
func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan *Retrieval) {
for {
// Allocate a new bloom bit index to retrieve data for, stopping when done
bit, ok := s.AllocateRetrieval()
bit, ok := s.allocateRetrieval()
if !ok {
return
}
// Bit allocated, throttle a bit if we're below our batch limit
if s.PendingSections(bit) < batch {
if s.pendingSections(bit) < batch {
select {
case <-s.quit:
// Session terminating, we can't meaningfully service, abort
s.AllocateSections(bit, 0)
s.DeliverSections(bit, []uint64{}, [][]byte{})
s.allocateSections(bit, 0)
s.deliverSections(bit, []uint64{}, [][]byte{})
return
case <-time.After(wait):
@@ -626,13 +615,13 @@ func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan
}
}
// Allocate as much as we can handle and request servicing
sections := s.AllocateSections(bit, batch)
sections := s.allocateSections(bit, batch)
request := make(chan *Retrieval)
select {
case <-s.quit:
// Session terminating, we can't meaningfully service, abort
s.DeliverSections(bit, sections, make([][]byte, len(sections)))
s.deliverSections(bit, sections, make([][]byte, len(sections)))
return
case mux <- request:
@@ -644,7 +633,7 @@ func (s *MatcherSession) Multiplex(batch int, wait time.Duration, mux chan chan
s.err.Store(result.Error)
s.Close()
}
s.DeliverSections(result.Bit, result.Sections, result.Bitsets)
s.deliverSections(result.Bit, result.Sections, result.Bitsets)
}
}
}

View File

@@ -46,6 +46,9 @@ type ChainIndexerBackend interface {
// Commit finalizes the section metadata and stores it into the database.
Commit() error
// Prune deletes the chain index older than the given threshold.
Prune(threshold uint64) error
}
// ChainIndexerChain interface is used for connecting the indexer to a blockchain
@@ -386,7 +389,6 @@ func (c *ChainIndexer) processSection(section uint64, lastHead common.Hash) (com
c.log.Trace("Processing new chain section", "section", section)
// Reset and partial processing
if err := c.backend.Reset(c.ctx, section, lastHead); err != nil {
c.setValidSections(0)
return common.Hash{}, err
@@ -459,6 +461,11 @@ func (c *ChainIndexer) AddChildIndexer(indexer *ChainIndexer) {
}
}
// Prune deletes all chain data older than given threshold.
func (c *ChainIndexer) Prune(threshold uint64) error {
return c.backend.Prune(threshold)
}
// loadValidSections reads the number of valid sections from the index database
// and caches is into the local state.
func (c *ChainIndexer) loadValidSections() {

View File

@@ -236,3 +236,7 @@ func (b *testChainIndexBackend) Commit() error {
}
return nil
}
func (b *testChainIndexBackend) Prune(threshold uint64) error {
return nil
}

View File

@@ -220,7 +220,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
if err != nil {
panic(fmt.Sprintf("state write error: %v", err))
}
if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
if err := statedb.Database().TrieDB().Commit(root, false, nil); err != nil {
panic(fmt.Sprintf("trie write error: %v", err))
}
return block, b.receipts

View File

@@ -79,7 +79,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
}
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil {
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
}
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
@@ -104,7 +104,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
}
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil {
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
}
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
@@ -130,7 +130,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
}
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil {
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
}
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
@@ -150,7 +150,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
if _, err := bc.InsertChain(blocks); err != nil {
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
}
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true, nil); err != nil {
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
}
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})

View File

@@ -36,6 +36,7 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
//go:generate gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
@@ -174,7 +175,7 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
// We have the genesis block in database(perhaps in ancient database)
// but the corresponding state is missing.
header := rawdb.ReadHeader(db, stored, 0)
if _, err := state.New(header.Root, state.NewDatabaseWithCache(db, 0), nil); err != nil {
if _, err := state.New(header.Root, state.NewDatabaseWithCache(db, 0, ""), nil); err != nil {
if genesis == nil {
genesis = DefaultGenesisBlock()
}
@@ -285,9 +286,9 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
head.Difficulty = params.GenesisDifficulty
}
statedb.Commit(false)
statedb.Database().TrieDB().Commit(root, true)
statedb.Database().TrieDB().Commit(root, true, nil)
return types.NewBlock(head, nil, nil, nil)
return types.NewBlock(head, nil, nil, nil, new(trie.Trie))
}
// Commit writes the block and state of a genesis specification to the database.
@@ -400,7 +401,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
return &Genesis{
Config: &config,
ExtraData: append(append(make([]byte, 32), faucet[:]...), make([]byte, crypto.SignatureLength)...),
GasLimit: 6283185,
GasLimit: 11500000,
Difficulty: big.NewInt(1),
Alloc: map[common.Address]GenesisAccount{
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
@@ -411,6 +412,7 @@ func DeveloperGenesisBlock(period uint64, faucet common.Address) *Genesis {
common.BytesToAddress([]byte{6}): {Balance: big.NewInt(1)}, // ECAdd
common.BytesToAddress([]byte{7}): {Balance: big.NewInt(1)}, // ECScalarMul
common.BytesToAddress([]byte{8}): {Balance: big.NewInt(1)}, // ECPairing
common.BytesToAddress([]byte{9}): {Balance: big.NewInt(1)}, // BLAKE2b
faucet: {Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))},
},
}

View File

@@ -488,8 +488,10 @@ func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
type (
// UpdateHeadBlocksCallback is a callback function that is called by SetHead
// before head header is updated.
UpdateHeadBlocksCallback func(ethdb.KeyValueWriter, *types.Header)
// before head header is updated. The method will return the actual block it
// updated the head to (missing state) and a flag if setHead should continue
// rewinding till that forcefully (exceeded ancient limits)
UpdateHeadBlocksCallback func(ethdb.KeyValueWriter, *types.Header) (uint64, bool)
// DeleteBlockContentCallback is a callback function that is called by SetHead
// before each header is deleted.
@@ -502,9 +504,10 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
var (
parentHash common.Hash
batch = hc.chainDb.NewBatch()
origin = true
)
for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() {
hash, num := hdr.Hash(), hdr.Number.Uint64()
num := hdr.Number.Uint64()
// Rewind block chain to new head.
parent := hc.GetHeader(hdr.ParentHash, num-1)
@@ -512,16 +515,21 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
parent = hc.genesisHeader
}
parentHash = hdr.ParentHash
// Notably, since geth has the possibility for setting the head to a low
// height which is even lower than ancient head.
// In order to ensure that the head is always no higher than the data in
// the database(ancient store or active store), we need to update head
// the database (ancient store or active store), we need to update head
// 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(markerBatch, parent)
newHead, force := updateFn(markerBatch, parent)
if force && newHead < head {
log.Warn("Force rewinding till ancient limit", "head", newHead)
head = newHead
}
}
// Update head header then.
rawdb.WriteHeadHeaderHash(markerBatch, parentHash)
@@ -532,14 +540,34 @@ func (hc *HeaderChain) SetHead(head uint64, updateFn UpdateHeadBlocksCallback, d
hc.currentHeaderHash = parentHash
headHeaderGauge.Update(parent.Number.Int64())
// Remove the relative data from the database.
if delFn != nil {
delFn(batch, hash, num)
// If this is the first iteration, wipe any leftover data upwards too so
// we don't end up with dangling daps in the database
var nums []uint64
if origin {
for n := num + 1; len(rawdb.ReadAllHashes(hc.chainDb, n)) > 0; n++ {
nums = append([]uint64{n}, nums...) // suboptimal, but we don't really expect this path
}
origin = false
}
nums = append(nums, num)
// Remove the related data from the database on all sidechains
for _, num := range nums {
// Gather all the side fork hashes
hashes := rawdb.ReadAllHashes(hc.chainDb, num)
if len(hashes) == 0 {
// No hashes in the database whatsoever, probably frozen already
hashes = append(hashes, hdr.Hash())
}
for _, hash := range hashes {
if delFn != nil {
delFn(batch, hash, num)
}
rawdb.DeleteHeader(batch, hash, num)
rawdb.DeleteTd(batch, hash, num)
}
rawdb.DeleteCanonicalHash(batch, num)
}
// Rewind header chain to new head.
rawdb.DeleteHeader(batch, hash, num)
rawdb.DeleteTd(batch, hash, num)
rawdb.DeleteCanonicalHash(batch, num)
}
// Flush all accumulated deletions.
if err := batch.Write(); err != nil {

View File

@@ -80,6 +80,39 @@ func ReadAllHashes(db ethdb.Iteratee, number uint64) []common.Hash {
return hashes
}
// ReadAllCanonicalHashes retrieves all canonical number and hash mappings at the
// certain chain range. If the accumulated entries reaches the given threshold,
// abort the iteration and return the semi-finish result.
func ReadAllCanonicalHashes(db ethdb.Iteratee, from uint64, to uint64, limit int) ([]uint64, []common.Hash) {
// Short circuit if the limit is 0.
if limit == 0 {
return nil, nil
}
var (
numbers []uint64
hashes []common.Hash
)
// Construct the key prefix of start point.
start, end := headerHashKey(from), headerHashKey(to)
it := db.NewIterator(nil, start)
defer it.Release()
for it.Next() {
if bytes.Compare(it.Key(), end) >= 0 {
break
}
if key := it.Key(); len(key) == len(headerPrefix)+8+1 && bytes.Equal(key[len(key)-1:], headerHashSuffix) {
numbers = append(numbers, binary.BigEndian.Uint64(key[len(headerPrefix):len(headerPrefix)+8]))
hashes = append(hashes, common.BytesToHash(it.Value()))
// If the accumulated entries reaches the limit threshold, return.
if len(numbers) >= limit {
break
}
}
}
return numbers, hashes
}
// ReadHeaderNumber returns the header number assigned to a hash.
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
data, _ := db.Get(headerNumberKey(hash))
@@ -154,6 +187,32 @@ func WriteHeadFastBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
}
}
// ReadLastPivotNumber retrieves the number of the last pivot block. If the node
// full synced, the last pivot will always be nil.
func ReadLastPivotNumber(db ethdb.KeyValueReader) *uint64 {
data, _ := db.Get(lastPivotKey)
if len(data) == 0 {
return nil
}
var pivot uint64
if err := rlp.DecodeBytes(data, &pivot); err != nil {
log.Error("Invalid pivot block number in database", "err", err)
return nil
}
return &pivot
}
// WriteLastPivotNumber stores the number of the last pivot block.
func WriteLastPivotNumber(db ethdb.KeyValueWriter, pivot uint64) {
enc, err := rlp.EncodeToBytes(pivot)
if err != nil {
log.Crit("Failed to encode pivot block number", "err", err)
}
if err := db.Put(lastPivotKey, enc); err != nil {
log.Crit("Failed to store pivot block number", "err", err)
}
}
// ReadFastTrieProgress retrieves the number of tries nodes fast synced to allow
// reporting correct numbers across restarts.
func ReadFastTrieProgress(db ethdb.KeyValueReader) uint64 {

View File

@@ -23,6 +23,7 @@ import (
"io/ioutil"
"math/big"
"os"
"reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -83,7 +84,7 @@ func TestBodyStorage(t *testing.T) {
WriteBody(db, hash, 0, body)
if entry := ReadBody(db, hash, 0); entry == nil {
t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(types.Transactions(body.Transactions)) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
} else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(types.Transactions(body.Transactions), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(body.Uncles) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, body)
}
if entry := ReadBodyRLP(db, hash, 0); entry == nil {
@@ -137,7 +138,7 @@ func TestBlockStorage(t *testing.T) {
}
if entry := ReadBody(db, block.Hash(), block.NumberU64()); entry == nil {
t.Fatalf("Stored body not found")
} else if types.DeriveSha(types.Transactions(entry.Transactions)) != types.DeriveSha(block.Transactions()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
} else if types.DeriveSha(types.Transactions(entry.Transactions), newHasher()) != types.DeriveSha(block.Transactions(), newHasher()) || types.CalcUncleHash(entry.Uncles) != types.CalcUncleHash(block.Uncles()) {
t.Fatalf("Retrieved body mismatch: have %v, want %v", entry, block.Body())
}
// Delete the block and verify the execution
@@ -424,3 +425,35 @@ func TestAncientStorage(t *testing.T) {
t.Fatalf("invalid td returned")
}
}
func TestCanonicalHashIteration(t *testing.T) {
var cases = []struct {
from, to uint64
limit int
expect []uint64
}{
{1, 8, 0, nil},
{1, 8, 1, []uint64{1}},
{1, 8, 10, []uint64{1, 2, 3, 4, 5, 6, 7}},
{1, 9, 10, []uint64{1, 2, 3, 4, 5, 6, 7, 8}},
{2, 9, 10, []uint64{2, 3, 4, 5, 6, 7, 8}},
{9, 10, 10, nil},
}
// Test empty db iteration
db := NewMemoryDatabase()
numbers, _ := ReadAllCanonicalHashes(db, 0, 10, 10)
if len(numbers) != 0 {
t.Fatalf("No entry should be returned to iterate an empty db")
}
// Fill database with testing data.
for i := uint64(1); i <= 8; i++ {
WriteCanonicalHash(db, common.Hash{}, i)
WriteTd(db, common.Hash{}, i, big.NewInt(10)) // Write some interferential data
}
for i, c := range cases {
numbers, _ := ReadAllCanonicalHashes(db, c.from, c.to, c.limit)
if !reflect.DeepEqual(numbers, c.expect) {
t.Fatalf("Case %d failed, want %v, got %v", i, c.expect, numbers)
}
}
}

View File

@@ -17,6 +17,7 @@
package rawdb
import (
"bytes"
"math/big"
"github.com/ethereum/go-ethereum/common"
@@ -151,3 +152,24 @@ func WriteBloomBits(db ethdb.KeyValueWriter, bit uint, section uint64, head comm
log.Crit("Failed to store bloom bits", "err", err)
}
}
// DeleteBloombits removes all compressed bloom bits vector belonging to the
// given section range and bit index.
func DeleteBloombits(db ethdb.Database, bit uint, from uint64, to uint64) {
start, end := bloomBitsKey(bit, from, common.Hash{}), bloomBitsKey(bit, to, common.Hash{})
it := db.NewIterator(nil, start)
defer it.Release()
for it.Next() {
if bytes.Compare(it.Key(), end) >= 0 {
break
}
if len(it.Key()) != len(bloomBitsPrefix)+2+8+32 {
continue
}
db.Delete(it.Key())
}
if it.Error() != nil {
log.Crit("Failed to delete bloom bits", "err", it.Error())
}
}

View File

@@ -17,15 +17,43 @@
package rawdb
import (
"bytes"
"hash"
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
// testHasher is the helper tool for transaction/receipt list hashing.
// The original hasher is trie, in order to get rid of import cycle,
// use the testing hasher instead.
type testHasher struct {
hasher hash.Hash
}
func newHasher() *testHasher {
return &testHasher{hasher: sha3.NewLegacyKeccak256()}
}
func (h *testHasher) Reset() {
h.hasher.Reset()
}
func (h *testHasher) Update(key, val []byte) {
h.hasher.Write(key)
h.hasher.Write(val)
}
func (h *testHasher) Hash() common.Hash {
return common.BytesToHash(h.hasher.Sum(nil))
}
// Tests that positional lookup metadata can be stored and retrieved.
func TestLookupStorage(t *testing.T) {
tests := []struct {
@@ -71,7 +99,7 @@ func TestLookupStorage(t *testing.T) {
tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33})
txs := []*types.Transaction{tx1, tx2, tx3}
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil)
block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil, newHasher())
// Check that no transactions entries are in a pristine database
for i, tx := range txs {
@@ -106,3 +134,46 @@ func TestLookupStorage(t *testing.T) {
})
}
}
func TestDeleteBloomBits(t *testing.T) {
// Prepare testing data
db := NewMemoryDatabase()
for i := uint(0); i < 2; i++ {
for s := uint64(0); s < 2; s++ {
WriteBloomBits(db, i, s, params.MainnetGenesisHash, []byte{0x01, 0x02})
WriteBloomBits(db, i, s, params.RinkebyGenesisHash, []byte{0x01, 0x02})
}
}
check := func(bit uint, section uint64, head common.Hash, exist bool) {
bits, _ := ReadBloomBits(db, bit, section, head)
if exist && !bytes.Equal(bits, []byte{0x01, 0x02}) {
t.Fatalf("Bloombits mismatch")
}
if !exist && len(bits) > 0 {
t.Fatalf("Bloombits should be removed")
}
}
// Check the existence of written data.
check(0, 0, params.MainnetGenesisHash, true)
check(0, 0, params.RinkebyGenesisHash, true)
// Check the existence of deleted data.
DeleteBloombits(db, 0, 0, 1)
check(0, 0, params.MainnetGenesisHash, false)
check(0, 0, params.RinkebyGenesisHash, false)
check(0, 1, params.MainnetGenesisHash, true)
check(0, 1, params.RinkebyGenesisHash, true)
// Check the existence of deleted data.
DeleteBloombits(db, 0, 0, 2)
check(0, 0, params.MainnetGenesisHash, false)
check(0, 0, params.RinkebyGenesisHash, false)
check(0, 1, params.MainnetGenesisHash, false)
check(0, 1, params.RinkebyGenesisHash, false)
// Bit1 shouldn't be affect.
check(1, 0, params.MainnetGenesisHash, true)
check(1, 0, params.RinkebyGenesisHash, true)
check(1, 1, params.MainnetGenesisHash, true)
check(1, 1, params.RinkebyGenesisHash, true)
}

View File

@@ -79,20 +79,3 @@ func WriteChainConfig(db ethdb.KeyValueWriter, hash common.Hash, cfg *params.Cha
log.Crit("Failed to store chain config", "err", err)
}
}
// ReadPreimage retrieves a single preimage of the provided hash.
func ReadPreimage(db ethdb.KeyValueReader, hash common.Hash) []byte {
data, _ := db.Get(preimageKey(hash))
return data
}
// WritePreimages writes the provided set of preimages to the database.
func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
for hash, preimage := range preimages {
if err := db.Put(preimageKey(hash), preimage); err != nil {
log.Crit("Failed to store trie preimage", "err", err)
}
}
preimageCounter.Inc(int64(len(preimages)))
preimageHitCounter.Inc(int64(len(preimages)))
}

View File

@@ -0,0 +1,96 @@
// Copyright 2020 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 rawdb
import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/log"
)
// ReadPreimage retrieves a single preimage of the provided hash.
func ReadPreimage(db ethdb.KeyValueReader, hash common.Hash) []byte {
data, _ := db.Get(preimageKey(hash))
return data
}
// WritePreimages writes the provided set of preimages to the database.
func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
for hash, preimage := range preimages {
if err := db.Put(preimageKey(hash), preimage); err != nil {
log.Crit("Failed to store trie preimage", "err", err)
}
}
preimageCounter.Inc(int64(len(preimages)))
preimageHitCounter.Inc(int64(len(preimages)))
}
// ReadCode retrieves the contract code of the provided code hash.
func ReadCode(db ethdb.KeyValueReader, hash common.Hash) []byte {
// Try with the legacy code scheme first, if not then try with current
// scheme. Since most of the code will be found with legacy scheme.
//
// todo(rjl493456442) change the order when we forcibly upgrade the code
// scheme with snapshot.
data, _ := db.Get(hash[:])
if len(data) != 0 {
return data
}
return ReadCodeWithPrefix(db, hash)
}
// ReadCodeWithPrefix retrieves the contract code of the provided code hash.
// The main difference between this function and ReadCode is this function
// will only check the existence with latest scheme(with prefix).
func ReadCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) []byte {
data, _ := db.Get(codeKey(hash))
return data
}
// WriteCode writes the provided contract code database.
func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
if err := db.Put(codeKey(hash), code); err != nil {
log.Crit("Failed to store contract code", "err", err)
}
}
// DeleteCode deletes the specified contract code from the database.
func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Delete(codeKey(hash)); err != nil {
log.Crit("Failed to delete contract code", "err", err)
}
}
// ReadTrieNode retrieves the trie node of the provided hash.
func ReadTrieNode(db ethdb.KeyValueReader, hash common.Hash) []byte {
data, _ := db.Get(hash.Bytes())
return data
}
// WriteTrieNode writes the provided trie node database.
func WriteTrieNode(db ethdb.KeyValueWriter, hash common.Hash, node []byte) {
if err := db.Put(hash.Bytes(), node); err != nil {
log.Crit("Failed to store trie node", "err", err)
}
}
// DeleteTrieNode deletes the specified trie node from the database.
func DeleteTrieNode(db ethdb.KeyValueWriter, hash common.Hash) {
if err := db.Delete(hash.Bytes()); err != nil {
log.Crit("Failed to delete trie node", "err", err)
}
}

View File

@@ -34,11 +34,11 @@ func TestChainIterator(t *testing.T) {
var txs []*types.Transaction
for i := uint64(0); i <= 10; i++ {
if i == 0 {
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, nil, nil, nil) // Empty genesis block
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, nil, nil, nil, newHasher()) // Empty genesis block
} else {
tx := types.NewTransaction(i, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11})
txs = append(txs, tx)
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil)
block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, []*types.Transaction{tx}, nil, nil, newHasher())
}
WriteBlock(chainDb, block)
WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64())

View File

@@ -21,6 +21,7 @@ import (
"errors"
"fmt"
"os"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -53,6 +54,22 @@ func (frdb *freezerdb) Close() error {
return nil
}
// Freeze is a helper method used for external testing to trigger and block until
// a freeze cycle completes, without having to sleep for a minute to trigger the
// automatic background run.
func (frdb *freezerdb) Freeze(threshold uint64) {
// Set the freezer threshold to a temporary value
defer func(old uint64) {
atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, old)
}(atomic.LoadUint64(&frdb.AncientStore.(*freezer).threshold))
atomic.StoreUint64(&frdb.AncientStore.(*freezer).threshold, threshold)
// Trigger a freeze cycle and block until it's done
trigger := make(chan struct{}, 1)
frdb.AncientStore.(*freezer).trigger <- trigger
<-trigger
}
// nofreezedb is a database wrapper that disables freezer data retrievals.
type nofreezedb struct {
ethdb.KeyValueStore
@@ -137,7 +154,10 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace st
// If the freezer already contains something, ensure that the genesis blocks
// match, otherwise we might mix up freezers across chains and destroy both
// the freezer and the key-value store.
if frgenesis, _ := frdb.Ancient(freezerHashTable, 0); !bytes.Equal(kvgenesis, frgenesis) {
frgenesis, err := frdb.Ancient(freezerHashTable, 0)
if err != nil {
return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
} else if !bytes.Equal(kvgenesis, frgenesis) {
return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis)
}
// Key-value store and freezer belong to the same network. Ensure that they
@@ -238,6 +258,7 @@ func InspectDatabase(db ethdb.Database) error {
numHashPairing common.StorageSize
hashNumPairing common.StorageSize
trieSize common.StorageSize
codeSize common.StorageSize
txlookupSize common.StorageSize
accountSnapSize common.StorageSize
storageSnapSize common.StorageSize
@@ -296,6 +317,8 @@ func InspectDatabase(db ethdb.Database) error {
chtTrieNodes += size
case bytes.HasPrefix(key, []byte("blt-")) && len(key) == 4+common.HashLength:
bloomTrieNodes += size
case bytes.HasPrefix(key, codePrefix) && len(key) == len(codePrefix)+common.HashLength:
codeSize += size
case len(key) == common.HashLength:
trieSize += size
default:
@@ -335,6 +358,7 @@ func InspectDatabase(db ethdb.Database) error {
{"Key-Value store", "Block hash->number", hashNumPairing.String()},
{"Key-Value store", "Transaction index", txlookupSize.String()},
{"Key-Value store", "Bloombit index", bloomBitsSize.String()},
{"Key-Value store", "Contract codes", codeSize.String()},
{"Key-Value store", "Trie nodes", trieSize.String()},
{"Key-Value store", "Trie preimages", preimageSize.String()},
{"Key-Value store", "Account snapshot", accountSnapSize.String()},

View File

@@ -22,6 +22,7 @@ import (
"math"
"os"
"path/filepath"
"sync"
"sync/atomic"
"time"
@@ -69,11 +70,16 @@ type freezer struct {
// WARNING: The `frozen` field is accessed atomically. On 32 bit platforms, only
// 64-bit aligned fields can be atomic. The struct is guaranteed to be so aligned,
// so take advantage of that (https://golang.org/pkg/sync/atomic/#pkg-note-BUG).
frozen uint64 // Number of blocks already frozen
frozen uint64 // Number of blocks already frozen
threshold uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests)
tables map[string]*freezerTable // Data tables for storing everything
instanceLock fileutil.Releaser // File-system lock to prevent double opens
quit chan struct{}
trigger chan chan struct{} // Manual blocking freeze trigger, test determinism
quit chan struct{}
closeOnce sync.Once
}
// newFreezer creates a chain freezer that moves ancient chain data into
@@ -100,8 +106,10 @@ func newFreezer(datadir string, namespace string) (*freezer, error) {
}
// Open all the supported data tables
freezer := &freezer{
threshold: params.FullImmutabilityThreshold,
tables: make(map[string]*freezerTable),
instanceLock: lock,
trigger: make(chan chan struct{}),
quit: make(chan struct{}),
}
for name, disableSnappy := range freezerNoSnappy {
@@ -128,16 +136,18 @@ func newFreezer(datadir string, namespace string) (*freezer, error) {
// Close terminates the chain freezer, unmapping all the data files.
func (f *freezer) Close() error {
f.quit <- struct{}{}
var errs []error
for _, table := range f.tables {
if err := table.Close(); err != nil {
f.closeOnce.Do(func() {
f.quit <- struct{}{}
for _, table := range f.tables {
if err := table.Close(); err != nil {
errs = append(errs, err)
}
}
if err := f.instanceLock.Release(); err != nil {
errs = append(errs, err)
}
}
if err := f.instanceLock.Release(); err != nil {
errs = append(errs, err)
}
})
if errs != nil {
return fmt.Errorf("%v", errs)
}
@@ -221,7 +231,7 @@ func (f *freezer) AppendAncient(number uint64, hash, header, body, receipts, td
return nil
}
// Truncate discards any recent data above the provided threshold number.
// TruncateAncients discards any recent data above the provided threshold number.
func (f *freezer) TruncateAncients(items uint64) error {
if atomic.LoadUint64(&f.frozen) <= items {
return nil
@@ -235,7 +245,7 @@ func (f *freezer) TruncateAncients(items uint64) error {
return nil
}
// sync flushes all data tables to disk.
// Sync flushes all data tables to disk.
func (f *freezer) Sync() error {
var errs []error
for _, table := range f.tables {
@@ -257,7 +267,10 @@ func (f *freezer) Sync() error {
func (f *freezer) freeze(db ethdb.KeyValueStore) {
nfdb := &nofreezedb{KeyValueStore: db}
backoff := false
var (
backoff bool
triggered chan struct{} // Used in tests
)
for {
select {
case <-f.quit:
@@ -266,9 +279,16 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
default:
}
if backoff {
// If we were doing a manual trigger, notify it
if triggered != nil {
triggered <- struct{}{}
triggered = nil
}
select {
case <-time.NewTimer(freezerRecheckInterval).C:
backoff = false
case triggered = <-f.trigger:
backoff = false
case <-f.quit:
return
}
@@ -281,18 +301,20 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
continue
}
number := ReadHeaderNumber(nfdb, hash)
threshold := atomic.LoadUint64(&f.threshold)
switch {
case number == nil:
log.Error("Current full block number unavailable", "hash", hash)
backoff = true
continue
case *number < params.ImmutabilityThreshold:
log.Debug("Current full block not old enough", "number", *number, "hash", hash, "delay", params.ImmutabilityThreshold)
case *number < threshold:
log.Debug("Current full block not old enough", "number", *number, "hash", hash, "delay", threshold)
backoff = true
continue
case *number-params.ImmutabilityThreshold <= f.frozen:
case *number-threshold <= f.frozen:
log.Debug("Ancient blocks frozen already", "number", *number, "hash", hash, "frozen", f.frozen)
backoff = true
continue
@@ -304,7 +326,7 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
continue
}
// Seems we have data ready to be frozen, process in usable batches
limit := *number - params.ImmutabilityThreshold
limit := *number - threshold
if limit-f.frozen > freezerBatchLimit {
limit = f.frozen + freezerBatchLimit
}
@@ -313,7 +335,7 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
first = f.frozen
ancients = make([]common.Hash, 0, limit-f.frozen)
)
for f.frozen < limit {
for f.frozen <= limit {
// Retrieves all the components of the canonical block
hash := ReadCanonicalHash(nfdb, f.frozen)
if hash == (common.Hash{}) {
@@ -364,11 +386,15 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
log.Crit("Failed to delete frozen canonical blocks", "err", err)
}
batch.Reset()
// Wipe out side chain also.
// Wipe out side chains also and track dangling side chians
var dangling []common.Hash
for number := first; number < f.frozen; number++ {
// Always keep the genesis block in active database
if number != 0 {
for _, hash := range ReadAllHashes(db, number) {
dangling = ReadAllHashes(db, number)
for _, hash := range dangling {
log.Trace("Deleting side chain", "number", number, "hash", hash)
DeleteBlock(batch, hash, number)
}
}
@@ -376,6 +402,41 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
if err := batch.Write(); err != nil {
log.Crit("Failed to delete frozen side blocks", "err", err)
}
batch.Reset()
// Step into the future and delete and dangling side chains
if f.frozen > 0 {
tip := f.frozen
for len(dangling) > 0 {
drop := make(map[common.Hash]struct{})
for _, hash := range dangling {
log.Debug("Dangling parent from freezer", "number", tip-1, "hash", hash)
drop[hash] = struct{}{}
}
children := ReadAllHashes(db, tip)
for i := 0; i < len(children); i++ {
// Dig up the child and ensure it's dangling
child := ReadHeader(nfdb, children[i], tip)
if child == nil {
log.Error("Missing dangling header", "number", tip, "hash", children[i])
continue
}
if _, ok := drop[child.ParentHash]; !ok {
children = append(children[:i], children[i+1:]...)
i--
continue
}
// Delete all block data associated with the child
log.Debug("Deleting dangling block", "number", tip, "hash", children[i], "parent", child.ParentHash)
DeleteBlock(batch, children[i], tip)
}
dangling = children
tip++
}
if err := batch.Write(); err != nil {
log.Crit("Failed to delete dangling side blocks", "err", err)
}
}
// Log something friendly for the user
context := []interface{}{
"blocks", f.frozen - first, "elapsed", common.PrettyDuration(time.Since(start)), "number", f.frozen - 1,

View File

@@ -330,7 +330,8 @@ func (t *freezerTable) truncate(items uint64) error {
defer t.lock.Unlock()
// If our item count is correct, don't do anything
if atomic.LoadUint64(&t.items) <= items {
existing := atomic.LoadUint64(&t.items)
if existing <= items {
return nil
}
// We need to truncate, save the old size for metrics tracking
@@ -339,7 +340,11 @@ func (t *freezerTable) truncate(items uint64) error {
return err
}
// Something's out of sync, truncate the table's offset index
t.logger.Warn("Truncating freezer table", "items", t.items, "limit", items)
log := t.logger.Debug
if existing > items+1 {
log = t.logger.Warn // Only loud warn if we delete multiple items
}
log("Truncating freezer table", "items", existing, "limit", items)
if err := truncateFreezerFile(t.index, int64(items+1)*indexEntrySize); err != nil {
return err
}

View File

@@ -18,6 +18,7 @@
package rawdb
import (
"bytes"
"encoding/binary"
"github.com/ethereum/go-ethereum/common"
@@ -38,6 +39,9 @@ var (
// headFastBlockKey tracks the latest known incomplete block's hash during fast sync.
headFastBlockKey = []byte("LastFast")
// lastPivotKey tracks the last pivot block used by fast sync (to reenable on sethead).
lastPivotKey = []byte("LastPivot")
// fastTrieProgressKey tracks the number of trie entries imported during fast sync.
fastTrieProgressKey = []byte("TrieSync")
@@ -66,6 +70,7 @@ var (
bloomBitsPrefix = []byte("B") // bloomBitsPrefix + bit (uint16 big endian) + section (uint64 big endian) + hash -> bloom bits
SnapshotAccountPrefix = []byte("a") // SnapshotAccountPrefix + account hash -> account trie value
SnapshotStoragePrefix = []byte("o") // SnapshotStoragePrefix + account hash + storage hash -> storage trie value
codePrefix = []byte("c") // codePrefix + code hash -> account code
preimagePrefix = []byte("secure-key-") // preimagePrefix + hash -> preimage
configPrefix = []byte("ethereum-config-") // config prefix for the db
@@ -189,6 +194,20 @@ func preimageKey(hash common.Hash) []byte {
return append(preimagePrefix, hash.Bytes()...)
}
// codeKey = codePrefix + hash
func codeKey(hash common.Hash) []byte {
return append(codePrefix, hash.Bytes()...)
}
// IsCodeKey reports whether the given byte slice is the key of contract code,
// if so return the raw code hash as well.
func IsCodeKey(key []byte) (bool, []byte) {
if bytes.HasPrefix(key, codePrefix) && len(key) == common.HashLength+len(codePrefix) {
return true, key[len(codePrefix):]
}
return false, nil
}
// configKey = configPrefix + hash
func configKey(hash common.Hash) []byte {
return append(configPrefix, hash.Bytes()...)

View File

@@ -17,9 +17,12 @@
package state
import (
"errors"
"fmt"
"github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru"
@@ -28,6 +31,9 @@ import (
const (
// Number of codehash->size associations to keep.
codeSizeCacheSize = 100000
// Cache size granted for caching clean code.
codeCacheSize = 64 * 1024 * 1024
)
// Database wraps access to tries and contract code.
@@ -100,23 +106,25 @@ type Trie interface {
// concurrent use, but does not retain any recent trie nodes in memory. To keep some
// historical state in memory, use the NewDatabaseWithCache constructor.
func NewDatabase(db ethdb.Database) Database {
return NewDatabaseWithCache(db, 0)
return NewDatabaseWithCache(db, 0, "")
}
// NewDatabaseWithCache creates a backing store for state. The returned database
// is safe for concurrent use and retains a lot of collapsed RLP trie nodes in a
// large memory cache.
func NewDatabaseWithCache(db ethdb.Database, cache int) Database {
func NewDatabaseWithCache(db ethdb.Database, cache int, journal string) Database {
csc, _ := lru.New(codeSizeCacheSize)
return &cachingDB{
db: trie.NewDatabaseWithCache(db, cache),
db: trie.NewDatabaseWithCache(db, cache, journal),
codeSizeCache: csc,
codeCache: fastcache.New(codeCacheSize),
}
}
type cachingDB struct {
db *trie.Database
codeSizeCache *lru.Cache
codeCache *fastcache.Cache
}
// OpenTrie opens the main account trie at a specific root hash.
@@ -141,11 +149,32 @@ func (db *cachingDB) CopyTrie(t Trie) Trie {
// ContractCode retrieves a particular contract's code.
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
code, err := db.db.Node(codeHash)
if err == nil {
db.codeSizeCache.Add(codeHash, len(code))
if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
return code, nil
}
return code, err
code := rawdb.ReadCode(db.db.DiskDB(), codeHash)
if len(code) > 0 {
db.codeCache.Set(codeHash.Bytes(), code)
db.codeSizeCache.Add(codeHash, len(code))
return code, nil
}
return nil, errors.New("not found")
}
// ContractCodeWithPrefix retrieves a particular contract's code. If the
// code can't be found in the cache, then check the existence with **new**
// db scheme.
func (db *cachingDB) ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error) {
if code := db.codeCache.Get(nil, codeHash.Bytes()); len(code) > 0 {
return code, nil
}
code := rawdb.ReadCodeWithPrefix(db.db.DiskDB(), codeHash)
if len(code) > 0 {
db.codeCache.Set(codeHash.Bytes(), code)
db.codeSizeCache.Add(codeHash, len(code))
return code, nil
}
return nil, errors.New("not found")
}
// ContractCodeSize retrieves a particular contracts code's size.

View File

@@ -28,6 +28,7 @@ import (
func TestNodeIteratorCoverage(t *testing.T) {
// Create some arbitrary test state to iterate
db, root, _ := makeTestState()
db.TrieDB().Commit(root, false, nil)
state, err := New(root, db, nil)
if err != nil {
@@ -42,7 +43,10 @@ func TestNodeIteratorCoverage(t *testing.T) {
}
// Cross check the iterated hashes and the database/nodepool content
for hash := range hashes {
if _, err := db.TrieDB().Node(hash); err != nil {
if _, err = db.TrieDB().Node(hash); err != nil {
_, err = db.ContractCode(common.Hash{}, hash)
}
if err != nil {
t.Errorf("failed to retrieve reported node %x", hash)
}
}

View File

@@ -54,9 +54,11 @@ type generatorStats struct {
// Log creates an contextual log with the given message and the context pulled
// from the internally maintained statistics.
func (gs *generatorStats) Log(msg string, marker []byte) {
func (gs *generatorStats) Log(msg string, root common.Hash, marker []byte) {
var ctx []interface{}
if root != (common.Hash{}) {
ctx = append(ctx, []interface{}{"root", root}...)
}
// Figure out whether we're after or within an account
switch len(marker) {
case common.HashLength:
@@ -120,7 +122,7 @@ func generateSnapshot(diskdb ethdb.KeyValueStore, triedb *trie.Database, cache i
func (dl *diskLayer) generate(stats *generatorStats) {
// If a database wipe is in operation, wait until it's done
if stats.wiping != nil {
stats.Log("Wiper running, state snapshotting paused", dl.genMarker)
stats.Log("Wiper running, state snapshotting paused", common.Hash{}, dl.genMarker)
select {
// If wiper is done, resume normal mode of operation
case <-stats.wiping:
@@ -137,13 +139,13 @@ func (dl *diskLayer) generate(stats *generatorStats) {
accTrie, err := trie.NewSecure(dl.root, dl.triedb)
if err != nil {
// The account trie is missing (GC), surf the chain until one becomes available
stats.Log("Trie missing, state snapshotting paused", dl.genMarker)
stats.Log("Trie missing, state snapshotting paused", dl.root, dl.genMarker)
abort := <-dl.genAbort
abort <- stats
return
}
stats.Log("Resuming state snapshot generation", dl.genMarker)
stats.Log("Resuming state snapshot generation", dl.root, dl.genMarker)
var accMarker []byte
if len(dl.genMarker) > 0 { // []byte{} is the start, use nil for that
@@ -192,7 +194,7 @@ func (dl *diskLayer) generate(stats *generatorStats) {
dl.lock.Unlock()
}
if abort != nil {
stats.Log("Aborting state snapshot generation", accountHash[:])
stats.Log("Aborting state snapshot generation", dl.root, accountHash[:])
abort <- stats
return
}
@@ -230,7 +232,7 @@ func (dl *diskLayer) generate(stats *generatorStats) {
dl.lock.Unlock()
}
if abort != nil {
stats.Log("Aborting state snapshot generation", append(accountHash[:], storeIt.Key...))
stats.Log("Aborting state snapshot generation", dl.root, append(accountHash[:], storeIt.Key...))
abort <- stats
return
}
@@ -238,7 +240,7 @@ func (dl *diskLayer) generate(stats *generatorStats) {
}
}
if time.Since(logged) > 8*time.Second {
stats.Log("Generating state snapshot", accIt.Key)
stats.Log("Generating state snapshot", dl.root, accIt.Key)
logged = time.Now()
}
// Some account processed, unmark the marker

View File

@@ -193,7 +193,7 @@ func (dl *diskLayer) Journal(buffer *bytes.Buffer) (common.Hash, error) {
dl.genAbort <- abort
if stats = <-abort; stats != nil {
stats.Log("Journalling in-progress snapshot", dl.genMarker)
stats.Log("Journalling in-progress snapshot", dl.root, dl.genMarker)
}
}
// Ensure the layer didn't get stale

View File

@@ -263,6 +263,13 @@ func (t *Tree) Cap(root common.Hash, layers int) error {
if !ok {
return fmt.Errorf("snapshot [%#x] is disk layer", root)
}
// If the generator is still running, use a more aggressive cap
diff.origin.lock.RLock()
if diff.origin.genMarker != nil && layers > 8 {
layers = 8
}
diff.origin.lock.RUnlock()
// Run the internal capping and discard all stale layers
t.lock.Lock()
defer t.lock.Unlock()

View File

@@ -375,22 +375,21 @@ func (s *stateObject) CommitTrie(db Database) error {
return err
}
// AddBalance removes amount from c's balance.
// AddBalance adds amount to s's balance.
// It is used to add funds to the destination account of a transfer.
func (s *stateObject) AddBalance(amount *big.Int) {
// EIP158: We must check emptiness for the objects such that the account
// EIP161: We must check emptiness for the objects such that the account
// clearing (0,0,0 objects) can take effect.
if amount.Sign() == 0 {
if s.empty() {
s.touch()
}
return
}
s.SetBalance(new(big.Int).Add(s.Balance(), amount))
}
// SubBalance removes amount from c's balance.
// SubBalance removes amount from s's balance.
// It is used to remove funds from the origin account of a transfer.
func (s *stateObject) SubBalance(amount *big.Int) {
if amount.Sign() == 0 {
@@ -455,7 +454,7 @@ func (s *stateObject) Code(db Database) []byte {
}
// CodeSize returns the size of the contract code associated with this object,
// or zero if none. This methos is an almost mirror of Code, but uses a cache
// or zero if none. This method is an almost mirror of Code, but uses a cache
// inside the database to avoid loading codes seen recently.
func (s *stateObject) CodeSize(db Database) int {
if s.code != nil {

View File

@@ -25,6 +25,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@@ -42,9 +43,6 @@ type revision struct {
var (
// emptyRoot is the known root hash of an empty trie.
emptyRoot = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// emptyCode is the known hash of the empty EVM bytecode.
emptyCode = crypto.Keccak256Hash(nil)
)
type proofList [][]byte
@@ -58,7 +56,7 @@ func (n *proofList) Delete(key []byte) error {
panic("not supported")
}
// StateDBs within the ethereum protocol are used to store anything
// StateDB structs within the ethereum protocol are used to store anything
// within the merkle trie. StateDBs take care of caching and storing
// nested states. It's the general query interface to retrieve:
// * Contracts
@@ -115,7 +113,7 @@ type StateDB struct {
SnapshotCommits time.Duration
}
// Create a new state from a given trie.
// New creates a new state from a given trie.
func New(root common.Hash, db Database, snaps *snapshot.Tree) (*StateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
@@ -250,7 +248,7 @@ func (s *StateDB) Empty(addr common.Address) bool {
return so == nil || so.empty()
}
// Retrieve the balance from the given address or 0 if object not found
// GetBalance retrieves the balance from the given address or 0 if object not found
func (s *StateDB) GetBalance(addr common.Address) *big.Int {
stateObject := s.getStateObject(addr)
if stateObject != nil {
@@ -318,7 +316,7 @@ func (s *StateDB) GetProof(a common.Address) ([][]byte, error) {
return [][]byte(proof), err
}
// GetProof returns the StorageProof for given key
// GetStorageProof returns the StorageProof for given key
func (s *StateDB) GetStorageProof(a common.Address, key common.Hash) ([][]byte, error) {
var proof proofList
trie := s.StorageTrie(a)
@@ -505,7 +503,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
}
// If no live objects are available, attempt to use snapshots
var (
data Account
data *Account
err error
)
if s.snap != nil {
@@ -517,11 +515,15 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if acc == nil {
return nil
}
data.Nonce, data.Balance, data.CodeHash = acc.Nonce, acc.Balance, acc.CodeHash
data = &Account{
Nonce: acc.Nonce,
Balance: acc.Balance,
CodeHash: acc.CodeHash,
Root: common.BytesToHash(acc.Root),
}
if len(data.CodeHash) == 0 {
data.CodeHash = emptyCodeHash
}
data.Root = common.BytesToHash(acc.Root)
if data.Root == (common.Hash{}) {
data.Root = emptyRoot
}
@@ -540,13 +542,14 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if len(enc) == 0 {
return nil
}
if err := rlp.DecodeBytes(enc, &data); err != nil {
data = new(Account)
if err := rlp.DecodeBytes(enc, data); err != nil {
log.Error("Failed to decode state object", "addr", addr, "err", err)
return nil
}
}
// Insert into the live set
obj := newObject(s, addr, data)
obj := newObject(s, addr, *data)
s.setStateObject(obj)
return obj
}
@@ -555,7 +558,7 @@ func (s *StateDB) setStateObject(object *stateObject) {
s.stateObjects[object.Address()] = object
}
// Retrieve a state object or create a new state object if nil.
// GetOrNewStateObject retrieves a state object or create a new state object if nil.
func (s *StateDB) GetOrNewStateObject(addr common.Address) *stateObject {
stateObject := s.getStateObject(addr)
if stateObject == nil {
@@ -584,7 +587,10 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
s.journal.append(resetObjectChange{prev: prev, prevdestruct: prevdestruct})
}
s.setStateObject(newobj)
return newobj, prev
if prev != nil && !prev.deleted {
return newobj, prev
}
return newobj, nil
}
// CreateAccount explicitly creates a state object. If a state object with the address
@@ -811,11 +817,12 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
s.IntermediateRoot(deleteEmptyObjects)
// Commit objects to the trie, measuring the elapsed time
codeWriter := s.db.TrieDB().DiskDB().NewBatch()
for addr := range s.stateObjectsDirty {
if obj := s.stateObjects[addr]; !obj.deleted {
// Write any contract code associated with the state object
if obj.code != nil && obj.dirtyCode {
s.db.TrieDB().InsertBlob(common.BytesToHash(obj.CodeHash()), obj.code)
rawdb.WriteCode(codeWriter, common.BytesToHash(obj.CodeHash()), obj.code)
obj.dirtyCode = false
}
// Write any storage changes in the state object to its storage trie
@@ -827,6 +834,11 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
if len(s.stateObjectsDirty) > 0 {
s.stateObjectsDirty = make(map[common.Address]struct{})
}
if codeWriter.ValueSize() > 0 {
if err := codeWriter.Write(); err != nil {
log.Crit("Failed to commit dirty codes", "error", err)
}
}
// Write the account trie changes, measuing the amount of wasted time
var start time.Time
if metrics.EnabledExpensive {
@@ -835,17 +847,13 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
// The onleaf func is called _serially_, so we can reuse the same account
// for unmarshalling every time.
var account Account
root, err := s.trie.Commit(func(leaf []byte, parent common.Hash) error {
root, err := s.trie.Commit(func(path []byte, leaf []byte, parent common.Hash) error {
if err := rlp.DecodeBytes(leaf, &account); err != nil {
return nil
}
if account.Root != emptyRoot {
s.db.TrieDB().Reference(account.Root, parent)
}
code := common.BytesToHash(account.CodeHash)
if code != emptyCode {
s.db.TrieDB().Reference(code, parent)
}
return nil
})
if metrics.EnabledExpensive {

View File

@@ -55,7 +55,7 @@ func TestUpdateLeaks(t *testing.T) {
}
root := state.IntermediateRoot(false)
if err := state.Database().TrieDB().Commit(root, false); err != nil {
if err := state.Database().TrieDB().Commit(root, false, nil); err != nil {
t.Errorf("can not commit trie %v to persistent database", root.Hex())
}
@@ -106,7 +106,7 @@ func TestIntermediateLeaks(t *testing.T) {
if err != nil {
t.Fatalf("failed to commit transition state: %v", err)
}
if err = transState.Database().TrieDB().Commit(transRoot, false); err != nil {
if err = transState.Database().TrieDB().Commit(transRoot, false, nil); err != nil {
t.Errorf("can not commit trie %v to persistent database", transRoot.Hex())
}
@@ -114,7 +114,7 @@ func TestIntermediateLeaks(t *testing.T) {
if err != nil {
t.Fatalf("failed to commit final state: %v", err)
}
if err = finalState.Database().TrieDB().Commit(finalRoot, false); err != nil {
if err = finalState.Database().TrieDB().Commit(finalRoot, false, nil); err != nil {
t.Errorf("can not commit trie %v to persistent database", finalRoot.Hex())
}
@@ -144,7 +144,7 @@ func TestIntermediateLeaks(t *testing.T) {
}
}
// TestCopy tests that copying a statedb object indeed makes the original and
// TestCopy tests that copying a StateDB object indeed makes the original and
// the copy independent of each other. This test is a regression test against
// https://github.com/ethereum/go-ethereum/pull/15549.
func TestCopy(t *testing.T) {
@@ -647,11 +647,11 @@ func TestCopyCopyCommitCopy(t *testing.T) {
}
// TestDeleteCreateRevert tests a weird state transition corner case that we hit
// while changing the internals of statedb. The workflow is that a contract is
// self destructed, then in a followup transaction (but same block) it's created
// while changing the internals of StateDB. The workflow is that a contract is
// self-destructed, then in a follow-up transaction (but same block) it's created
// again and the transaction reverted.
//
// The original statedb implementation flushed dirty objects to the tries after
// The original StateDB implementation flushed dirty objects to the tries after
// each transaction, so this works ok. The rework accumulated writes in memory
// first, but the journal wiped the entire state object on create-revert.
func TestDeleteCreateRevert(t *testing.T) {
@@ -681,7 +681,7 @@ func TestDeleteCreateRevert(t *testing.T) {
}
}
// TestMissingTrieNodes tests that if the statedb fails to load parts of the trie,
// TestMissingTrieNodes tests that if the StateDB fails to load parts of the trie,
// the Commit operation fails with an error
// If we are missing trie nodes, we should not continue writing to the trie
func TestMissingTrieNodes(t *testing.T) {

View File

@@ -28,13 +28,13 @@ import (
// NewStateSync create a new state trie download scheduler.
func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.SyncBloom) *trie.Sync {
var syncer *trie.Sync
callback := func(leaf []byte, parent common.Hash) error {
callback := func(path []byte, leaf []byte, parent common.Hash) error {
var obj Account
if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil {
return err
}
syncer.AddSubTrie(obj.Root, 64, parent, nil)
syncer.AddRawEntry(common.BytesToHash(obj.CodeHash), 64, parent)
syncer.AddSubTrie(obj.Root, path, parent, nil)
syncer.AddCodeEntry(common.BytesToHash(obj.CodeHash), path, parent)
return nil
}
syncer = trie.NewSync(root, database, callback, bloom)

View File

@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
@@ -44,7 +45,7 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
state, _ := New(common.Hash{}, db, nil)
// Fill it with some arbitrary data
accounts := []*testAccount{}
var accounts []*testAccount
for i := byte(0); i < 96; i++ {
obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
acc := &testAccount{address: common.BytesToAddress([]byte{i})}
@@ -59,6 +60,11 @@ func makeTestState() (Database, common.Hash, []*testAccount) {
obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i})
acc.code = []byte{i, i, i, i, i}
}
if i%5 == 0 {
for j := byte(0); j < 5; j++ {
obj.SetState(db, crypto.Keccak256Hash([]byte{i, i, i, i, i, j, j}), crypto.Keccak256Hash([]byte{i, i, i, i, i, j, j}))
}
}
state.updateStateObject(obj)
accounts = append(accounts, acc)
}
@@ -126,43 +132,109 @@ func checkStateConsistency(db ethdb.Database, root common.Hash) error {
// Tests that an empty state is not scheduled for syncing.
func TestEmptyStateSync(t *testing.T) {
empty := common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
if req := NewStateSync(empty, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New())).Missing(1); len(req) != 0 {
t.Errorf("content requested for empty state: %v", req)
sync := NewStateSync(empty, rawdb.NewMemoryDatabase(), trie.NewSyncBloom(1, memorydb.New()))
if nodes, paths, codes := sync.Missing(1); len(nodes) != 0 || len(paths) != 0 || len(codes) != 0 {
t.Errorf(" content requested for empty state: %v, %v, %v", nodes, paths, codes)
}
}
// Tests that given a root hash, a state can sync iteratively on a single thread,
// requesting retrieval tasks and returning all of them in one go.
func TestIterativeStateSyncIndividual(t *testing.T) { testIterativeStateSync(t, 1) }
func TestIterativeStateSyncBatched(t *testing.T) { testIterativeStateSync(t, 100) }
func TestIterativeStateSyncIndividual(t *testing.T) {
testIterativeStateSync(t, 1, false, false)
}
func TestIterativeStateSyncBatched(t *testing.T) {
testIterativeStateSync(t, 100, false, false)
}
func TestIterativeStateSyncIndividualFromDisk(t *testing.T) {
testIterativeStateSync(t, 1, true, false)
}
func TestIterativeStateSyncBatchedFromDisk(t *testing.T) {
testIterativeStateSync(t, 100, true, false)
}
func TestIterativeStateSyncIndividualByPath(t *testing.T) {
testIterativeStateSync(t, 1, false, true)
}
func TestIterativeStateSyncBatchedByPath(t *testing.T) {
testIterativeStateSync(t, 100, false, true)
}
func testIterativeStateSync(t *testing.T, count int) {
func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
// Create a random state to copy
srcDb, srcRoot, srcAccounts := makeTestState()
if commit {
srcDb.TrieDB().Commit(srcRoot, false, nil)
}
srcTrie, _ := trie.New(srcRoot, srcDb.TrieDB())
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
queue := append([]common.Hash{}, sched.Missing(count)...)
for len(queue) > 0 {
results := make([]trie.SyncResult, len(queue))
for i, hash := range queue {
nodes, paths, codes := sched.Missing(count)
var (
hashQueue []common.Hash
pathQueue []trie.SyncPath
)
if !bypath {
hashQueue = append(append(hashQueue[:0], nodes...), codes...)
} else {
hashQueue = append(hashQueue[:0], codes...)
pathQueue = append(pathQueue[:0], paths...)
}
for len(hashQueue)+len(pathQueue) > 0 {
results := make([]trie.SyncResult, len(hashQueue)+len(pathQueue))
for i, hash := range hashQueue {
data, err := srcDb.TrieDB().Node(hash)
if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash)
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil {
t.Fatalf("failed to retrieve node data for hash %x", hash)
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
for i, path := range pathQueue {
if len(path) == 1 {
data, _, err := srcTrie.TryGetNode(path[0])
if err != nil {
t.Fatalf("failed to retrieve node data for path %x: %v", path, err)
}
results[len(hashQueue)+i] = trie.SyncResult{Hash: crypto.Keccak256Hash(data), Data: data}
} else {
var acc Account
if err := rlp.DecodeBytes(srcTrie.Get(path[0]), &acc); err != nil {
t.Fatalf("failed to decode account on path %x: %v", path, err)
}
stTrie, err := trie.New(acc.Root, srcDb.TrieDB())
if err != nil {
t.Fatalf("failed to retriev storage trie for path %x: %v", path, err)
}
data, _, err := stTrie.TryGetNode(path[1])
if err != nil {
t.Fatalf("failed to retrieve node data for path %x: %v", path, err)
}
results[len(hashQueue)+i] = trie.SyncResult{Hash: crypto.Keccak256Hash(data), Data: data}
}
}
for _, result := range results {
if err := sched.Process(result); err != nil {
t.Errorf("failed to process result %v", err)
}
}
batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil {
t.Fatalf("failed to commit data: %v", err)
}
batch.Write()
queue = append(queue[:0], sched.Missing(count)...)
nodes, paths, codes = sched.Missing(count)
if !bypath {
hashQueue = append(append(hashQueue[:0], nodes...), codes...)
} else {
hashQueue = append(hashQueue[:0], codes...)
pathQueue = append(pathQueue[:0], paths...)
}
}
// Cross check that the two states are in sync
checkStateAccounts(t, dstDb, srcRoot, srcAccounts)
@@ -178,26 +250,35 @@ func TestIterativeDelayedStateSync(t *testing.T) {
dstDb := rawdb.NewMemoryDatabase()
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
queue := append([]common.Hash{}, sched.Missing(0)...)
nodes, _, codes := sched.Missing(0)
queue := append(append([]common.Hash{}, nodes...), codes...)
for len(queue) > 0 {
// Sync only half of the scheduled nodes
results := make([]trie.SyncResult, len(queue)/2+1)
for i, hash := range queue[:len(results)] {
data, err := srcDb.TrieDB().Node(hash)
if err != nil {
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash)
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
for _, result := range results {
if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
}
batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil {
t.Fatalf("failed to commit data: %v", err)
}
batch.Write()
queue = append(queue[len(results):], sched.Missing(0)...)
nodes, _, codes = sched.Missing(0)
queue = append(append(queue[len(results):], nodes...), codes...)
}
// Cross check that the two states are in sync
checkStateAccounts(t, dstDb, srcRoot, srcAccounts)
@@ -218,7 +299,8 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
queue := make(map[common.Hash]struct{})
for _, hash := range sched.Missing(count) {
nodes, _, codes := sched.Missing(count)
for _, hash := range append(nodes, codes...) {
queue[hash] = struct{}{}
}
for len(queue) > 0 {
@@ -226,22 +308,29 @@ func testIterativeRandomStateSync(t *testing.T, count int) {
results := make([]trie.SyncResult, 0, len(queue))
for hash := range queue {
data, err := srcDb.TrieDB().Node(hash)
if err != nil {
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash)
}
results = append(results, trie.SyncResult{Hash: hash, Data: data})
}
// Feed the retrieved results back and queue new tasks
if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
for _, result := range results {
if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
}
batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil {
t.Fatalf("failed to commit data: %v", err)
}
batch.Write()
queue = make(map[common.Hash]struct{})
for _, hash := range sched.Missing(count) {
nodes, _, codes = sched.Missing(count)
for _, hash := range append(nodes, codes...) {
queue[hash] = struct{}{}
}
}
@@ -260,7 +349,8 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
queue := make(map[common.Hash]struct{})
for _, hash := range sched.Missing(0) {
nodes, _, codes := sched.Missing(0)
for _, hash := range append(nodes, codes...) {
queue[hash] = struct{}{}
}
for len(queue) > 0 {
@@ -270,6 +360,9 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
delete(queue, hash)
data, err := srcDb.TrieDB().Node(hash)
if err != nil {
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash)
}
@@ -280,15 +373,21 @@ func TestIterativeRandomDelayedStateSync(t *testing.T) {
}
}
// Feed the retrieved results back and queue new tasks
if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
for _, result := range results {
if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
}
batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil {
t.Fatalf("failed to commit data: %v", err)
}
batch.Write()
for _, hash := range sched.Missing(0) {
for _, result := range results {
delete(queue, result.Hash)
}
nodes, _, codes = sched.Missing(0)
for _, hash := range append(nodes, codes...) {
queue[hash] = struct{}{}
}
}
@@ -302,27 +401,44 @@ func TestIncompleteStateSync(t *testing.T) {
// Create a random state to copy
srcDb, srcRoot, srcAccounts := makeTestState()
// isCode reports whether the hash is contract code hash.
isCode := func(hash common.Hash) bool {
for _, acc := range srcAccounts {
if hash == crypto.Keccak256Hash(acc.code) {
return true
}
}
return false
}
checkTrieConsistency(srcDb.TrieDB().DiskDB().(ethdb.Database), srcRoot)
// Create a destination state and sync with the scheduler
dstDb := rawdb.NewMemoryDatabase()
sched := NewStateSync(srcRoot, dstDb, trie.NewSyncBloom(1, dstDb))
added := []common.Hash{}
queue := append([]common.Hash{}, sched.Missing(1)...)
var added []common.Hash
nodes, _, codes := sched.Missing(1)
queue := append(append([]common.Hash{}, nodes...), codes...)
for len(queue) > 0 {
// Fetch a batch of state nodes
results := make([]trie.SyncResult, len(queue))
for i, hash := range queue {
data, err := srcDb.TrieDB().Node(hash)
if err != nil {
data, err = srcDb.ContractCode(common.Hash{}, hash)
}
if err != nil {
t.Fatalf("failed to retrieve node data for %x", hash)
}
results[i] = trie.SyncResult{Hash: hash, Data: data}
}
// Process each of the state nodes
if _, index, err := sched.Process(results); err != nil {
t.Fatalf("failed to process result #%d: %v", index, err)
for _, result := range results {
if err := sched.Process(result); err != nil {
t.Fatalf("failed to process result %v", err)
}
}
batch := dstDb.NewBatch()
if err := sched.Commit(batch); err != nil {
@@ -333,12 +449,9 @@ func TestIncompleteStateSync(t *testing.T) {
added = append(added, result.Hash)
}
// Check that all known sub-tries added so far are complete or missing entirely.
checkSubtries:
for _, hash := range added {
for _, acc := range srcAccounts {
if hash == crypto.Keccak256Hash(acc.code) {
continue checkSubtries // skip trie check of code nodes.
}
if isCode(hash) {
continue
}
// Can't use checkStateConsistency here because subtrie keys may have odd
// length and crash in LeafKey.
@@ -347,17 +460,30 @@ func TestIncompleteStateSync(t *testing.T) {
}
}
// Fetch the next batch to retrieve
queue = append(queue[:0], sched.Missing(1)...)
nodes, _, codes = sched.Missing(1)
queue = append(append(queue[:0], nodes...), codes...)
}
// Sanity check that removing any node from the database is detected
for _, node := range added[1:] {
key := node.Bytes()
value, _ := dstDb.Get(key)
dstDb.Delete(key)
var (
key = node.Bytes()
code = isCode(node)
val []byte
)
if code {
val = rawdb.ReadCode(dstDb, node)
rawdb.DeleteCode(dstDb, node)
} else {
val = rawdb.ReadTrieNode(dstDb, node)
rawdb.DeleteTrieNode(dstDb, node)
}
if err := checkStateConsistency(dstDb, added[0]); err == nil {
t.Fatalf("trie inconsistency not caught, missing: %x", key)
}
dstDb.Put(key, value)
if code {
rawdb.WriteCode(dstDb, node, val)
} else {
rawdb.WriteTrieNode(dstDb, node, val)
}
}
}

View File

@@ -99,7 +99,30 @@ func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
// Filter iterates over the list of transactions and removes all of them for which
// the specified function evaluates to true.
// Filter, as opposed to 'filter', re-initialises the heap after the operation is done.
// If you want to do several consecutive filterings, it's therefore better to first
// do a .filter(func1) followed by .Filter(func2) or reheap()
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
removed := m.filter(filter)
// If transactions were removed, the heap and cache are ruined
if len(removed) > 0 {
m.reheap()
}
return removed
}
func (m *txSortedMap) reheap() {
*m.index = make([]uint64, 0, len(m.items))
for nonce := range m.items {
*m.index = append(*m.index, nonce)
}
heap.Init(m.index)
m.cache = nil
}
// filter is identical to Filter, but **does not** regenerate the heap. This method
// should only be used if followed immediately by a call to Filter or reheap()
func (m *txSortedMap) filter(filter func(*types.Transaction) bool) types.Transactions {
var removed types.Transactions
// Collect all the transactions to filter out
@@ -109,14 +132,7 @@ func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transac
delete(m.items, nonce)
}
}
// If transactions were removed, the heap and cache are ruined
if len(removed) > 0 {
*m.index = make([]uint64, 0, len(m.items))
for nonce := range m.items {
*m.index = append(*m.index, nonce)
}
heap.Init(m.index)
m.cache = nil
}
return removed
@@ -197,10 +213,7 @@ func (m *txSortedMap) Len() int {
return len(m.items)
}
// Flatten creates a nonce-sorted slice of transactions based on the loosely
// sorted internal representation. The result of the sorting is cached in case
// it's requested again before any modifications are made to the contents.
func (m *txSortedMap) Flatten() types.Transactions {
func (m *txSortedMap) flatten() types.Transactions {
// If the sorting was not cached yet, create and cache it
if m.cache == nil {
m.cache = make(types.Transactions, 0, len(m.items))
@@ -209,12 +222,27 @@ func (m *txSortedMap) Flatten() types.Transactions {
}
sort.Sort(types.TxByNonce(m.cache))
}
return m.cache
}
// Flatten creates a nonce-sorted slice of transactions based on the loosely
// sorted internal representation. The result of the sorting is cached in case
// it's requested again before any modifications are made to the contents.
func (m *txSortedMap) Flatten() types.Transactions {
// Copy the cache to prevent accidental modifications
txs := make(types.Transactions, len(m.cache))
copy(txs, m.cache)
cache := m.flatten()
txs := make(types.Transactions, len(cache))
copy(txs, cache)
return txs
}
// LastElement returns the last element of a flattened list, thus, the
// transaction with the highest nonce
func (m *txSortedMap) LastElement() *types.Transaction {
cache := m.flatten()
return cache[len(cache)-1]
}
// txList is a "list" of transactions belonging to an account, sorted by account
// nonce. The same type can be used both for storing contiguous transactions for
// the executable/pending queue; and for storing gapped transactions for the non-
@@ -252,7 +280,11 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
// If there's an older better transaction, abort
old := l.txs.Get(tx.Nonce())
if old != nil {
threshold := new(big.Int).Div(new(big.Int).Mul(old.GasPrice(), big.NewInt(100+int64(priceBump))), big.NewInt(100))
// threshold = oldGP * (100 + priceBump) / 100
a := big.NewInt(100 + int64(priceBump))
a = a.Mul(a, old.GasPrice())
b := big.NewInt(100)
threshold := a.Div(a, b)
// Have to ensure that the new gas price is higher than the old gas
// price as well as checking the percentage threshold to ensure that
// this is accurate for low (Wei-level) gas price replacements
@@ -296,20 +328,25 @@ func (l *txList) Filter(costLimit *big.Int, gasLimit uint64) (types.Transactions
l.gascap = gasLimit
// Filter out all the transactions above the account's funds
removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(costLimit) > 0 || tx.Gas() > gasLimit })
removed := l.txs.Filter(func(tx *types.Transaction) bool {
return tx.Gas() > gasLimit || tx.Cost().Cmp(costLimit) > 0
})
// If the list was strict, filter anything above the lowest nonce
if len(removed) == 0 {
return nil, nil
}
var invalids types.Transactions
if l.strict && len(removed) > 0 {
// If the list was strict, filter anything above the lowest nonce
if l.strict {
lowest := uint64(math.MaxUint64)
for _, tx := range removed {
if nonce := tx.Nonce(); lowest > nonce {
lowest = nonce
}
}
invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
invalids = l.txs.filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
}
l.txs.reheap()
return removed, invalids
}
@@ -363,6 +400,12 @@ func (l *txList) Flatten() types.Transactions {
return l.txs.Flatten()
}
// LastElement returns the last element of a flattened list, thus, the
// transaction with the highest nonce
func (l *txList) LastElement() *types.Transaction {
return l.txs.LastElement()
}
// priceHeap is a heap.Interface implementation over transactions for retrieving
// price-sorted transactions to discard when the pool fills up.
type priceHeap []*types.Transaction
@@ -495,8 +538,29 @@ func (l *txPricedList) Underpriced(tx *types.Transaction, local *accountSet) boo
// Discard finds a number of most underpriced transactions, removes them from the
// priced list and returns them for further removal from the entire pool.
func (l *txPricedList) Discard(slots int, local *accountSet) types.Transactions {
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
save := make(types.Transactions, 0, 64) // Local underpriced transactions to keep
// If we have some local accountset, those will not be discarded
if !local.empty() {
// In case the list is filled to the brim with 'local' txs, we do this
// little check to avoid unpacking / repacking the heap later on, which
// is very expensive
discardable := 0
for _, tx := range *l.items {
if !local.containsTx(tx) {
discardable++
}
if discardable >= slots {
break
}
}
if slots > discardable {
slots = discardable
}
}
if slots == 0 {
return nil
}
drop := make(types.Transactions, 0, slots) // Remote underpriced transactions to drop
save := make(types.Transactions, 0, len(*l.items)-slots) // Local underpriced transactions to keep
for len(*l.items) > 0 && slots > 0 {
// Discard stale transactions if found during cleanup

View File

@@ -98,6 +98,7 @@ var (
queuedReplaceMeter = metrics.NewRegisteredMeter("txpool/queued/replace", nil)
queuedRateLimitMeter = metrics.NewRegisteredMeter("txpool/queued/ratelimit", nil) // Dropped due to rate limiting
queuedNofundsMeter = metrics.NewRegisteredMeter("txpool/queued/nofunds", nil) // Dropped due to out-of-funds
queuedEvictionMeter = metrics.NewRegisteredMeter("txpool/queued/eviction", nil) // Dropped due to lifetime
// General tx metrics
knownTxMeter = metrics.NewRegisteredMeter("txpool/known", nil)
@@ -362,9 +363,11 @@ func (pool *TxPool) loop() {
}
// Any non-locals old enough should be removed
if time.Since(pool.beats[addr]) > pool.config.Lifetime {
for _, tx := range pool.queue[addr].Flatten() {
list := pool.queue[addr].Flatten()
for _, tx := range list {
pool.removeTx(tx.Hash(), true)
}
queuedEvictionMeter.Mark(int64(len(list)))
}
}
pool.mu.Unlock()
@@ -614,6 +617,9 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
pool.journalTx(from, tx)
pool.queueTxEvent(tx)
log.Trace("Pooled new executable transaction", "hash", hash, "from", from, "to", tx.To())
// Successful promotion, bump the heartbeat
pool.beats[from] = time.Now()
return old != nil, nil
}
// New transaction isn't replacing a pending one, push into queue
@@ -665,6 +671,10 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) (bool, er
pool.all.Add(tx)
pool.priced.Put(tx)
}
// If we never record the heartbeat, do it right now.
if _, exist := pool.beats[from]; !exist {
pool.beats[from] = time.Now()
}
return old != nil, nil
}
@@ -696,7 +706,6 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
// An older transaction was better, discard this
pool.all.Remove(hash)
pool.priced.Removed(1)
pendingDiscardMeter.Mark(1)
return false
}
@@ -704,7 +713,6 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
if old != nil {
pool.all.Remove(old.Hash())
pool.priced.Removed(1)
pendingReplaceMeter.Mark(1)
} else {
// Nothing was replaced, bump the pending counter
@@ -716,9 +724,10 @@ func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.T
pool.priced.Put(tx)
}
// Set the potentially new pending nonce and notify any subsystems of the new tx
pool.beats[addr] = time.Now()
pool.pendingNonces.set(addr, tx.Nonce()+1)
// Successful promotion, bump the heartbeat
pool.beats[addr] = time.Now()
return true
}
@@ -891,7 +900,6 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
// If no more pending transactions are left, remove the list
if pending.Empty() {
delete(pool.pending, addr)
delete(pool.beats, addr)
}
// Postpone any invalidated transactions
for _, tx := range invalids {
@@ -912,6 +920,7 @@ func (pool *TxPool) removeTx(hash common.Hash, outofbound bool) {
}
if future.Empty() {
delete(pool.queue, addr)
delete(pool.beats, addr)
}
}
}
@@ -1023,7 +1032,10 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
defer close(done)
var promoteAddrs []common.Address
if dirtyAccounts != nil {
if dirtyAccounts != nil && reset == nil {
// Only dirty accounts need to be promoted, unless we're resetting.
// For resets, all addresses in the tx queue will be promoted and
// the flatten operation can be avoided.
promoteAddrs = dirtyAccounts.flatten()
}
pool.mu.Lock()
@@ -1039,7 +1051,7 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
}
}
// Reset needs promote for all addresses
promoteAddrs = promoteAddrs[:0]
promoteAddrs = make([]common.Address, 0, len(pool.queue))
for addr := range pool.queue {
promoteAddrs = append(promoteAddrs, addr)
}
@@ -1059,8 +1071,8 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
// Update all accounts to the latest known pending nonce
for addr, list := range pool.pending {
txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway
pool.pendingNonces.set(addr, txs[len(txs)-1].Nonce()+1)
highestPending := list.LastElement()
pool.pendingNonces.set(addr, highestPending.Nonce()+1)
}
pool.mu.Unlock()
@@ -1229,6 +1241,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
// Delete the entire queue entry if it became empty.
if list.Empty() {
delete(pool.queue, addr)
delete(pool.beats, addr)
}
}
return promoted
@@ -1409,11 +1422,12 @@ func (pool *TxPool) demoteUnexecutables() {
pool.enqueueTx(hash, tx)
}
pendingGauge.Dec(int64(len(gapped)))
// This might happen in a reorg, so log it to the metering
blockReorgInvalidatedTx.Mark(int64(len(gapped)))
}
// Delete the entire queue entry if it became empty.
// Delete the entire pending entry if it became empty.
if list.Empty() {
delete(pool.pending, addr)
delete(pool.beats, addr)
}
}
}
@@ -1457,6 +1471,10 @@ func (as *accountSet) contains(addr common.Address) bool {
return exist
}
func (as *accountSet) empty() bool {
return len(as.accounts) == 0
}
// containsTx checks if the sender of a given tx is within the set. If the sender
// cannot be derived, this method returns false.
func (as *accountSet) containsTx(tx *types.Transaction) bool {

View File

@@ -34,6 +34,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
)
// testTxPoolConfig is a transaction pool configuration without stateful disk
@@ -54,7 +55,7 @@ type testBlockChain struct {
func (bc *testBlockChain) CurrentBlock() *types.Block {
return types.NewBlock(&types.Header{
GasLimit: bc.gasLimit,
}, nil, nil, nil)
}, nil, nil, nil, new(trie.Trie))
}
func (bc *testBlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
@@ -109,6 +110,7 @@ func validateTxPoolInternals(pool *TxPool) error {
if priced := pool.priced.items.Len() - pool.priced.stales; priced != pending+queued {
return fmt.Errorf("total priced transaction count %d != %d pending + %d queued", priced, pending, queued)
}
// Ensure the next nonce to assign is the correct one
for addr, txs := range pool.pending {
// Find the last transaction
@@ -868,7 +870,7 @@ func TestTransactionQueueTimeLimitingNoLocals(t *testing.T) {
func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
// Reduce the eviction interval to a testable amount
defer func(old time.Duration) { evictionInterval = old }(evictionInterval)
evictionInterval = time.Second
evictionInterval = time.Millisecond * 100
// Create the pool to test the non-expiration enforcement
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
@@ -905,6 +907,22 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Allow the eviction interval to run
time.Sleep(2 * evictionInterval)
// Transactions should not be evicted from the queue yet since lifetime duration has not passed
pending, queued = pool.Stats()
if pending != 0 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
}
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 2)
}
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Wait a bit for eviction to run and clean up any leftovers, and ensure only the local remains
time.Sleep(2 * config.Lifetime)
@@ -924,6 +942,72 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// remove current transactions and increase nonce to prepare for a reset and cleanup
statedb.SetNonce(crypto.PubkeyToAddress(remote.PublicKey), 2)
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 2)
<-pool.requestReset(nil, nil)
// make sure queue, pending are cleared
pending, queued = pool.Stats()
if pending != 0 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 0)
}
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// Queue gapped transactions
if err := pool.AddLocal(pricedTransaction(4, 100000, big.NewInt(1), local)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
if err := pool.addRemoteSync(pricedTransaction(4, 100000, big.NewInt(1), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
time.Sleep(5 * evictionInterval) // A half lifetime pass
// Queue executable transactions, the life cycle should be restarted.
if err := pool.AddLocal(pricedTransaction(2, 100000, big.NewInt(1), local)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
if err := pool.addRemoteSync(pricedTransaction(2, 100000, big.NewInt(1), remote)); err != nil {
t.Fatalf("failed to add remote transaction: %v", err)
}
time.Sleep(6 * evictionInterval)
// All gapped transactions shouldn't be kicked out
pending, queued = pool.Stats()
if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
}
if queued != 2 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 3)
}
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
// The whole life time pass after last promotion, kick out stale transactions
time.Sleep(2 * config.Lifetime)
pending, queued = pool.Stats()
if pending != 2 {
t.Fatalf("pending transactions mismatched: have %d, want %d", pending, 2)
}
if nolocals {
if queued != 0 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 0)
}
} else {
if queued != 1 {
t.Fatalf("queued transactions mismatched: have %d, want %d", queued, 1)
}
}
if err := validateTxPoolInternals(pool); err != nil {
t.Fatalf("pool internal state corrupted: %v", err)
}
}
// Tests that even if the transaction count belonging to a single account goes
@@ -1890,11 +1974,15 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
}
// Benchmarks the speed of batched transaction insertion.
func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100) }
func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000) }
func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000) }
func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, false) }
func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, false) }
func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, false) }
func benchmarkPoolBatchInsert(b *testing.B, size int) {
func BenchmarkPoolBatchLocalInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100, true) }
func BenchmarkPoolBatchLocalInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000, true) }
func BenchmarkPoolBatchLocalInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000, true) }
func benchmarkPoolBatchInsert(b *testing.B, size int, local bool) {
// Generate a batch of transactions to enqueue into the pool
pool, key := setupTxPool()
defer pool.Stop()
@@ -1912,6 +2000,10 @@ func benchmarkPoolBatchInsert(b *testing.B, size int) {
// Benchmark importing the transactions into the queue
b.ResetTimer()
for _, batch := range batches {
pool.AddRemotes(batch)
if local {
pool.AddLocals(batch)
} else {
pool.AddRemotes(batch)
}
}
}

View File

@@ -35,7 +35,7 @@ import (
)
var (
EmptyRootHash = DeriveSha(Transactions{})
EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
EmptyUncleHash = rlpHash([]*Header(nil))
)
@@ -147,6 +147,17 @@ func rlpHash(x interface{}) (h common.Hash) {
return h
}
// EmptyBody returns true if there is no additional 'body' to complete the header
// that is: no transactions and no uncles.
func (h *Header) EmptyBody() bool {
return h.TxHash == EmptyRootHash && h.UncleHash == EmptyUncleHash
}
// EmptyReceipts returns true if there are no receipts for this header/block.
func (h *Header) EmptyReceipts() bool {
return h.ReceiptHash == EmptyRootHash
}
// Body is a simple (mutable, non-safe) data container for storing and moving
// a block's data contents (transactions and uncles) together.
type Body struct {
@@ -210,14 +221,14 @@ type storageblock struct {
// The values of TxHash, UncleHash, ReceiptHash and Bloom in header
// are ignored and set to values derived from the given txs, uncles
// and receipts.
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt) *Block {
func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*Receipt, hasher Hasher) *Block {
b := &Block{header: CopyHeader(header), td: new(big.Int)}
// TODO: panic if len(txs) != len(receipts)
if len(txs) == 0 {
b.header.TxHash = EmptyRootHash
} else {
b.header.TxHash = DeriveSha(Transactions(txs))
b.header.TxHash = DeriveSha(Transactions(txs), hasher)
b.transactions = make(Transactions, len(txs))
copy(b.transactions, txs)
}
@@ -225,7 +236,7 @@ func NewBlock(header *Header, txs []*Transaction, uncles []*Header, receipts []*
if len(receipts) == 0 {
b.header.ReceiptHash = EmptyRootHash
} else {
b.header.ReceiptHash = DeriveSha(Receipts(receipts))
b.header.ReceiptHash = DeriveSha(Receipts(receipts), hasher)
b.header.Bloom = CreateBloom(receipts)
}

View File

@@ -18,6 +18,7 @@ package types
import (
"bytes"
"hash"
"math/big"
"reflect"
"testing"
@@ -27,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
)
// from bcValidBlockTest.json, "SimpleTx"
@@ -90,6 +92,30 @@ func BenchmarkEncodeBlock(b *testing.B) {
}
}
// testHasher is the helper tool for transaction/receipt list hashing.
// The original hasher is trie, in order to get rid of import cycle,
// use the testing hasher instead.
type testHasher struct {
hasher hash.Hash
}
func newHasher() *testHasher {
return &testHasher{hasher: sha3.NewLegacyKeccak256()}
}
func (h *testHasher) Reset() {
h.hasher.Reset()
}
func (h *testHasher) Update(key, val []byte) {
h.hasher.Write(key)
h.hasher.Write(val)
}
func (h *testHasher) Hash() common.Hash {
return common.BytesToHash(h.hasher.Sum(nil))
}
func makeBenchBlock() *Block {
var (
key, _ = crypto.GenerateKey()
@@ -128,5 +154,5 @@ func makeBenchBlock() *Block {
Extra: []byte("benchmark uncle"),
}
}
return NewBlock(header, txs, uncles, receipts)
return NewBlock(header, txs, uncles, receipts, newHasher())
}

View File

@@ -21,21 +21,28 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
// DerivableList is the interface which can derive the hash.
type DerivableList interface {
Len() int
GetRlp(i int) []byte
}
func DeriveSha(list DerivableList) common.Hash {
// Hasher is the tool used to calculate the hash of derivable list.
type Hasher interface {
Reset()
Update([]byte, []byte)
Hash() common.Hash
}
func DeriveSha(list DerivableList, hasher Hasher) common.Hash {
hasher.Reset()
keybuf := new(bytes.Buffer)
trie := new(trie.Trie)
for i := 0; i < list.Len(); i++ {
keybuf.Reset()
rlp.Encode(keybuf, uint(i))
trie.Update(keybuf.Bytes(), list.GetRlp(i))
hasher.Update(keybuf.Bytes(), list.GetRlp(i))
}
return trie.Hash()
return hasher.Hash()
}

View File

@@ -20,9 +20,9 @@ func (l Log) MarshalJSON() ([]byte, error) {
Data hexutil.Bytes `json:"data" gencodec:"required"`
BlockNumber hexutil.Uint64 `json:"blockNumber"`
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
TxIndex hexutil.Uint `json:"transactionIndex" gencodec:"required"`
TxIndex hexutil.Uint `json:"transactionIndex"`
BlockHash common.Hash `json:"blockHash"`
Index hexutil.Uint `json:"logIndex" gencodec:"required"`
Index hexutil.Uint `json:"logIndex"`
Removed bool `json:"removed"`
}
var enc Log
@@ -46,9 +46,9 @@ func (l *Log) UnmarshalJSON(input []byte) error {
Data *hexutil.Bytes `json:"data" gencodec:"required"`
BlockNumber *hexutil.Uint64 `json:"blockNumber"`
TxHash *common.Hash `json:"transactionHash" gencodec:"required"`
TxIndex *hexutil.Uint `json:"transactionIndex" gencodec:"required"`
TxIndex *hexutil.Uint `json:"transactionIndex"`
BlockHash *common.Hash `json:"blockHash"`
Index *hexutil.Uint `json:"logIndex" gencodec:"required"`
Index *hexutil.Uint `json:"logIndex"`
Removed *bool `json:"removed"`
}
var dec Log
@@ -74,17 +74,15 @@ func (l *Log) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'transactionHash' for Log")
}
l.TxHash = *dec.TxHash
if dec.TxIndex == nil {
return errors.New("missing required field 'transactionIndex' for Log")
if dec.TxIndex != nil {
l.TxIndex = uint(*dec.TxIndex)
}
l.TxIndex = uint(*dec.TxIndex)
if dec.BlockHash != nil {
l.BlockHash = *dec.BlockHash
}
if dec.Index == nil {
return errors.New("missing required field 'logIndex' for Log")
if dec.Index != nil {
l.Index = uint(*dec.Index)
}
l.Index = uint(*dec.Index)
if dec.Removed != nil {
l.Removed = *dec.Removed
}

View File

@@ -44,11 +44,11 @@ type Log struct {
// hash of the transaction
TxHash common.Hash `json:"transactionHash" gencodec:"required"`
// index of the transaction in the block
TxIndex uint `json:"transactionIndex" gencodec:"required"`
TxIndex uint `json:"transactionIndex"`
// hash of the block in which the transaction was included
BlockHash common.Hash `json:"blockHash"`
// index of the log in the block
Index uint `json:"logIndex" gencodec:"required"`
Index uint `json:"logIndex"`
// The Removed field is true if this log was reverted due to a chain reorganisation.
// You must pay attention to this field if you receive logs through a filter query.

View File

@@ -22,6 +22,7 @@ import (
"io"
"math/big"
"sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
@@ -36,7 +37,9 @@ var (
)
type Transaction struct {
data txdata
data txdata // Consensus contents of a transaction
time time.Time // Time first seen locally (spam avoidance)
// caches
hash atomic.Value
size atomic.Value
@@ -100,8 +103,10 @@ func newTransaction(nonce uint64, to *common.Address, amount *big.Int, gasLimit
if gasPrice != nil {
d.Price.Set(gasPrice)
}
return &Transaction{data: d}
return &Transaction{
data: d,
time: time.Now(),
}
}
// ChainId returns which chain id this transaction was signed for (if at all)
@@ -134,8 +139,8 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
err := s.Decode(&tx.data)
if err == nil {
tx.size.Store(common.StorageSize(rlp.ListSize(size)))
tx.time = time.Now()
}
return err
}
@@ -153,7 +158,6 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
if err := dec.UnmarshalJSON(input); err != nil {
return err
}
withSignature := dec.V.Sign() != 0 || dec.R.Sign() != 0 || dec.S.Sign() != 0
if withSignature {
var V byte
@@ -167,8 +171,10 @@ func (tx *Transaction) UnmarshalJSON(input []byte) error {
return ErrInvalidSig
}
}
*tx = Transaction{data: dec}
*tx = Transaction{
data: dec,
time: time.Now(),
}
return nil
}
@@ -246,7 +252,10 @@ func (tx *Transaction) WithSignature(signer Signer, sig []byte) (*Transaction, e
if err != nil {
return nil, err
}
cpy := &Transaction{data: tx.data}
cpy := &Transaction{
data: tx.data,
time: tx.time,
}
cpy.data.R, cpy.data.S, cpy.data.V = r, s, v
return cpy, nil
}
@@ -306,19 +315,27 @@ func (s TxByNonce) Len() int { return len(s) }
func (s TxByNonce) Less(i, j int) bool { return s[i].data.AccountNonce < s[j].data.AccountNonce }
func (s TxByNonce) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// TxByPrice implements both the sort and the heap interface, making it useful
// TxByPriceAndTime implements both the sort and the heap interface, making it useful
// for all at once sorting as well as individually adding and removing elements.
type TxByPrice Transactions
type TxByPriceAndTime Transactions
func (s TxByPrice) Len() int { return len(s) }
func (s TxByPrice) Less(i, j int) bool { return s[i].data.Price.Cmp(s[j].data.Price) > 0 }
func (s TxByPrice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s TxByPriceAndTime) Len() int { return len(s) }
func (s TxByPriceAndTime) Less(i, j int) bool {
// If the prices are equal, use the time the transaction was first seen for
// deterministic sorting
cmp := s[i].data.Price.Cmp(s[j].data.Price)
if cmp == 0 {
return s[i].time.Before(s[j].time)
}
return cmp > 0
}
func (s TxByPriceAndTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s *TxByPrice) Push(x interface{}) {
func (s *TxByPriceAndTime) Push(x interface{}) {
*s = append(*s, x.(*Transaction))
}
func (s *TxByPrice) Pop() interface{} {
func (s *TxByPriceAndTime) Pop() interface{} {
old := *s
n := len(old)
x := old[n-1]
@@ -331,7 +348,7 @@ func (s *TxByPrice) Pop() interface{} {
// entire batches of transactions for non-executable accounts.
type TransactionsByPriceAndNonce struct {
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
heads TxByPrice // Next transaction for each unique account (price heap)
heads TxByPriceAndTime // Next transaction for each unique account (price heap)
signer Signer // Signer for the set of transactions
}
@@ -341,8 +358,8 @@ type TransactionsByPriceAndNonce struct {
// Note, the input map is reowned so the caller should not interact any more with
// if after providing it to the constructor.
func NewTransactionsByPriceAndNonce(signer Signer, txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
// Initialize a price based heap with the head transactions
heads := make(TxByPrice, 0, len(txs))
// Initialize a price and received time based heap with the head transactions
heads := make(TxByPriceAndTime, 0, len(txs))
for from, accTxs := range txs {
heads = append(heads, accTxs[0])
// Ensure the sender address is from the signer

View File

@@ -22,6 +22,7 @@ import (
"encoding/json"
"math/big"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
@@ -127,8 +128,8 @@ func TestTransactionPriceNonceSort(t *testing.T) {
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
}
signer := HomesteadSigner{}
// Generate a batch of transactions with overlapping values, but shifted nonces
groups := map[common.Address]Transactions{}
for start, key := range keys {
@@ -155,12 +156,10 @@ func TestTransactionPriceNonceSort(t *testing.T) {
// Make sure the nonce order is valid
for j, txj := range txs[i+1:] {
fromj, _ := Sender(signer, txj)
if fromi == fromj && txi.Nonce() > txj.Nonce() {
t.Errorf("invalid nonce ordering: tx #%d (A=%x N=%v) < tx #%d (A=%x N=%v)", i, fromi[:4], txi.Nonce(), i+j, fromj[:4], txj.Nonce())
}
}
// If the next tx has different from account, the price must be lower than the current one
if i+1 < len(txs) {
next := txs[i+1]
@@ -172,6 +171,54 @@ func TestTransactionPriceNonceSort(t *testing.T) {
}
}
// Tests that if multiple transactions have the same price, the ones seen earlier
// are prioritized to avoid network spam attacks aiming for a specific ordering.
func TestTransactionTimeSort(t *testing.T) {
// Generate a batch of accounts to start with
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
}
signer := HomesteadSigner{}
// Generate a batch of transactions with overlapping prices, but different creation times
groups := map[common.Address]Transactions{}
for start, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
tx, _ := SignTx(NewTransaction(0, common.Address{}, big.NewInt(100), 100, big.NewInt(1), nil), signer, key)
tx.time = time.Unix(0, int64(len(keys)-start))
groups[addr] = append(groups[addr], tx)
}
// Sort the transactions and cross check the nonce ordering
txset := NewTransactionsByPriceAndNonce(signer, groups)
txs := Transactions{}
for tx := txset.Peek(); tx != nil; tx = txset.Peek() {
txs = append(txs, tx)
txset.Shift()
}
if len(txs) != len(keys) {
t.Errorf("expected %d transactions, found %d", len(keys), len(txs))
}
for i, txi := range txs {
fromi, _ := Sender(signer, txi)
if i+1 < len(txs) {
next := txs[i+1]
fromNext, _ := Sender(signer, next)
if txi.GasPrice().Cmp(next.GasPrice()) < 0 {
t.Errorf("invalid gasprice ordering: tx #%d (A=%x P=%v) < tx #%d (A=%x P=%v)", i, fromi[:4], txi.GasPrice(), i+1, fromNext[:4], next.GasPrice())
}
// Make sure time order is ascending if the txs have the same gas price
if txi.GasPrice().Cmp(next.GasPrice()) == 0 && txi.time.After(next.time) {
t.Errorf("invalid received time ordering: tx #%d (A=%x T=%v) > tx #%d (A=%x T=%v)", i, fromi[:4], txi.time, i+1, fromNext[:4], next.time)
}
}
}
}
// TestTransactionJSON tests serializing/de-serializing to/from JSON.
func TestTransactionJSON(t *testing.T) {
key, err := crypto.GenerateKey()

View File

@@ -112,7 +112,13 @@ func (c *Contract) validJumpSubdest(udest uint64) bool {
// isCode returns true if the provided PC location is an actual opcode, as
// opposed to a data-segment following a PUSHN operation.
func (c *Contract) isCode(udest uint64) bool {
// Do we already have an analysis laying around?
if c.analysis != nil {
return c.analysis.codeSegment(udest)
}
// Do we have a contract hash already?
// If we do have a hash, that means it's a 'regular' contract. For regular
// contracts ( not temporary initcode), we store the analysis in a map
if c.CodeHash != (common.Hash{}) {
// Does parent context have the analysis?
analysis, exist := c.jumpdests[c.CodeHash]

View File

@@ -102,12 +102,18 @@ var PrecompiledContractsYoloV1 = map[common.Address]PrecompiledContract{
}
// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
func RunPrecompiledContract(p PrecompiledContract, input []byte, contract *Contract) (ret []byte, err error) {
gas := p.RequiredGas(input)
if contract.UseGas(gas) {
return p.Run(input)
// It returns
// - the returned bytes,
// - the _remaining_ gas,
// - any error that occurred
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
gasCost := p.RequiredGas(input)
if suppliedGas < gasCost {
return nil, 0, ErrOutOfGas
}
return nil, ErrOutOfGas
suppliedGas -= gasCost
output, err := p.Run(input)
return output, suppliedGas, err
}
// ECRECOVER implemented as a native contract.
@@ -197,6 +203,7 @@ func (c *dataCopy) Run(in []byte) ([]byte, error) {
type bigModExp struct{}
var (
big0 = big.NewInt(0)
big1 = big.NewInt(1)
big4 = big.NewInt(4)
big8 = big.NewInt(8)

View File

@@ -21,7 +21,6 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"math/big"
"testing"
"time"
@@ -72,10 +71,9 @@ var blake2FMalformedInputTests = []precompiledFailureTest{
func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input)
contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), p.RequiredGas(in))
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) {
if res, err := RunPrecompiledContract(p, in, contract); err != nil {
gas := p.RequiredGas(in)
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
if res, _, err := RunPrecompiledContract(p, in, gas); err != nil {
t.Error(err)
} else if common.Bytes2Hex(res) != test.Expected {
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
@@ -91,10 +89,10 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input)
contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), p.RequiredGas(in)-1)
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(t *testing.T) {
_, err := RunPrecompiledContract(p, in, contract)
gas := p.RequiredGas(in) - 1
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
_, _, err := RunPrecompiledContract(p, in, gas)
if err.Error() != "out of gas" {
t.Errorf("Expected error [out of gas], got [%v]", err)
}
@@ -109,11 +107,9 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing.T) {
p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input)
contract := NewContract(AccountRef(common.HexToAddress("31337")),
nil, new(big.Int), p.RequiredGas(in))
gas := p.RequiredGas(in)
t.Run(test.Name, func(t *testing.T) {
_, err := RunPrecompiledContract(p, in, contract)
_, _, err := RunPrecompiledContract(p, in, gas)
if err.Error() != test.ExpectedError {
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
}
@@ -132,8 +128,6 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
p := allPrecompiles[common.HexToAddress(addr)]
in := common.Hex2Bytes(test.Input)
reqGas := p.RequiredGas(in)
contract := NewContract(AccountRef(common.HexToAddress("1337")),
nil, new(big.Int), reqGas)
var (
res []byte
@@ -141,14 +135,13 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
data = make([]byte, len(in))
)
bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, contract.Gas), func(bench *testing.B) {
bench.Run(fmt.Sprintf("%s-Gas=%d", test.Name, reqGas), func(bench *testing.B) {
bench.ReportAllocs()
start := time.Now().Nanosecond()
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
contract.Gas = reqGas
copy(data, in)
res, err = RunPrecompiledContract(p, data, contract)
res, _, err = RunPrecompiledContract(p, data, reqGas)
}
bench.StopTimer()
elapsed := float64(time.Now().Nanosecond() - start)

View File

@@ -68,12 +68,11 @@ func enable1884(jt *JumpTable) {
jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884
// New opcode
jt[SELFBALANCE] = operation{
jt[SELFBALANCE] = &operation{
execute: opSelfBalance,
constantGas: GasFastStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
valid: true,
}
}
@@ -87,12 +86,11 @@ func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, callContext *callCtx
// - Adds an opcode that returns the current chains EIP-155 unique identifier
func enable1344(jt *JumpTable) {
// New opcode
jt[CHAINID] = operation{
jt[CHAINID] = &operation{
execute: opChainID,
constantGas: GasQuickStep,
minStack: minStack(0, 1),
maxStack: maxStack(0, 1),
valid: true,
}
}
@@ -113,29 +111,26 @@ func enable2200(jt *JumpTable) {
// - Adds opcodes that jump to and return from subroutines
func enable2315(jt *JumpTable) {
// New opcode
jt[BEGINSUB] = operation{
jt[BEGINSUB] = &operation{
execute: opBeginSub,
constantGas: GasQuickStep,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
valid: true,
}
// New opcode
jt[JUMPSUB] = operation{
jt[JUMPSUB] = &operation{
execute: opJumpSub,
constantGas: GasSlowStep,
minStack: minStack(1, 0),
maxStack: maxStack(1, 0),
jumps: true,
valid: true,
}
// New opcode
jt[RETURNSUB] = operation{
jt[RETURNSUB] = &operation{
execute: opReturnSub,
constantGas: GasFastStep,
minStack: minStack(0, 0),
maxStack: maxStack(0, 0),
valid: true,
jumps: true,
}
}

View File

@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/holiman/uint256"
)
// emptyCodeHash is used by create to ensure deployment is disallowed to already
@@ -41,23 +42,24 @@ type (
GetHashFunc func(uint64) common.Hash
)
func (evm *EVM) precompile(addr common.Address) (PrecompiledContract, bool) {
var precompiles map[common.Address]PrecompiledContract
switch {
case evm.chainRules.IsYoloV1:
precompiles = PrecompiledContractsYoloV1
case evm.chainRules.IsIstanbul:
precompiles = PrecompiledContractsIstanbul
case evm.chainRules.IsByzantium:
precompiles = PrecompiledContractsByzantium
default:
precompiles = PrecompiledContractsHomestead
}
p, ok := precompiles[addr]
return p, ok
}
// run runs the given contract and takes care of running precompiles with a fallback to the byte code interpreter.
func run(evm *EVM, contract *Contract, input []byte, readOnly bool) ([]byte, error) {
if contract.CodeAddr != nil {
precompiles := PrecompiledContractsHomestead
if evm.chainRules.IsByzantium {
precompiles = PrecompiledContractsByzantium
}
if evm.chainRules.IsIstanbul {
precompiles = PrecompiledContractsIstanbul
}
if evm.chainRules.IsYoloV1 {
precompiles = PrecompiledContractsYoloV1
}
if p := precompiles[*contract.CodeAddr]; p != nil {
return RunPrecompiledContract(p, input, contract)
}
}
for _, interpreter := range evm.interpreters {
if interpreter.CanRun(contract.Code) {
if evm.interpreter != interpreter {
@@ -199,22 +201,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
return nil, gas, ErrDepth
}
// Fail if we're trying to transfer more than the available balance
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
var (
to = AccountRef(addr)
snapshot = evm.StateDB.Snapshot()
)
snapshot := evm.StateDB.Snapshot()
p, isPrecompile := evm.precompile(addr)
if !evm.StateDB.Exist(addr) {
precompiles := PrecompiledContractsHomestead
if evm.chainRules.IsByzantium {
precompiles = PrecompiledContractsByzantium
}
if evm.chainRules.IsIstanbul {
precompiles = PrecompiledContractsIstanbul
}
if precompiles[addr] == nil && evm.chainRules.IsEIP158 && value.Sign() == 0 {
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
@@ -224,35 +218,47 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
}
evm.StateDB.CreateAccount(addr)
}
evm.Transfer(evm.StateDB, caller.Address(), to.Address(), value)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
// Even if the account has no code, we need to continue because it might be a precompile
start := time.Now()
evm.Transfer(evm.StateDB, caller.Address(), addr, value)
// Capture the tracer start/end events in debug mode
if evm.vmConfig.Debug && evm.depth == 0 {
evm.vmConfig.Tracer.CaptureStart(caller.Address(), addr, false, input, gas, value)
defer func() { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
}()
defer func(startGas uint64, startTime time.Time) { // Lazy evaluation of the parameters
evm.vmConfig.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
}(gas, time.Now())
}
ret, err = run(evm, contract, input, false)
if isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
code := evm.StateDB.GetCode(addr)
if len(code) == 0 {
ret, err = nil, nil // gas is unchanged
} else {
addrCopy := addr
// If the account has no code, we can abort here
// The depth-check is already done, and precompiles handled above
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
ret, err = run(evm, contract, input, false)
gas = contract.Gas
}
}
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in homestead this also counts for code storage gas errors.
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
gas = 0
}
// TODO: consider clearing up unused snapshots:
//} else {
// evm.StateDB.DiscardSnapshot(snapshot)
}
return ret, contract.Gas, err
return ret, gas, err
}
// CallCode executes the contract associated with the addr with the given input
@@ -277,23 +283,27 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
return nil, gas, ErrInsufficientBalance
}
var (
snapshot = evm.StateDB.Snapshot()
to = AccountRef(caller.Address())
)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, value, gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
var snapshot = evm.StateDB.Snapshot()
ret, err = run(evm, contract, input, false)
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
addrCopy := addr
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = run(evm, contract, input, false)
gas = contract.Gas
}
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
gas = 0
}
}
return ret, contract.Gas, err
return ret, gas, err
}
// DelegateCall executes the contract associated with the addr with the given input
@@ -309,22 +319,26 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
var (
snapshot = evm.StateDB.Snapshot()
to = AccountRef(caller.Address())
)
// Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, to, nil, gas).AsDelegate()
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
var snapshot = evm.StateDB.Snapshot()
ret, err = run(evm, contract, input, false)
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
addrCopy := addr
// Initialise a new contract and make initialise the delegate values
contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = run(evm, contract, input, false)
gas = contract.Gas
}
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
gas = 0
}
}
return ret, contract.Gas, err
return ret, gas, err
}
// StaticCall executes the contract associated with the addr with the given input
@@ -339,32 +353,43 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
if evm.depth > int(params.CallCreateDepth) {
return nil, gas, ErrDepth
}
var (
to = AccountRef(addr)
snapshot = evm.StateDB.Snapshot()
)
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, to, new(big.Int), gas)
contract.SetCallCode(&addr, evm.StateDB.GetCodeHash(addr), evm.StateDB.GetCode(addr))
// We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
// However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced
// after all empty accounts were deleted, so this is not required. However, if we omit this,
// then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
// We could change this, but for now it's left for legacy reasons
var snapshot = evm.StateDB.Snapshot()
// We do an AddBalance of zero here, just in order to trigger a touch.
// This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
// but is the correct thing to do and matters on other networks, in tests, and potential
// future scenarios
evm.StateDB.AddBalance(addr, big.NewInt(0))
evm.StateDB.AddBalance(addr, big0)
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors.
ret, err = run(evm, contract, input, true)
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
// At this point, we use a copy of address. If we don't, the go compiler will
// leak the 'contract' to the outer scope, and make allocation for 'contract'
// even if the actual execution ends on RunPrecompiled above.
addrCopy := addr
// Initialise a new contract and set the code that is to be used by the EVM.
// The contract is a scoped environment for this execution context only.
contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
// When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors.
ret, err = run(evm, contract, input, true)
gas = contract.Gas
}
if err != nil {
evm.StateDB.RevertToSnapshot(snapshot)
if err != ErrExecutionReverted {
contract.UseGas(contract.Gas)
gas = 0
}
}
return ret, contract.Gas, err
return ret, gas, err
}
type codeAndHash struct {
@@ -466,9 +491,9 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
//
// The different between Create2 with Create is Create2 uses sha3(0xff ++ msg.sender ++ salt ++ sha3(init_code))[12:]
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
func (evm *EVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
codeAndHash := &codeAndHash{code: code}
contractAddr = crypto.CreateAddress2(caller.Address(), common.BigToHash(salt), codeAndHash.Hash().Bytes())
contractAddr = crypto.CreateAddress2(caller.Address(), common.Hash(salt.Bytes32()), codeAndHash.Hash().Bytes())
return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
}

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