Compare commits

...

72 Commits

Author SHA1 Message Date
Jeffrey Wilcke
81d9d7d385 Merge pull request #3252 from obscuren/release/1.4
1.4 HF
2016-11-15 13:46:47 +01:00
Jeffrey Wilcke
822355f8a6 VERSION, cmd/geth: version bump 1.4.19 2016-11-15 12:40:42 +01:00
Jeffrey Wilcke
7d9c6f611a params: Hardfork block number 2016-11-15 12:40:42 +01:00
Jeffrey Wilcke
dc2e34ddf3 core, core/state, trie: Hardfork EIP155, EIP161, EIP170
This commit implements EIP158 part 1, 2, 3 & 4

1. If an account is empty it's no longer written to the trie. An empty
  account is defined as (balance=0, nonce=0, storage=0, code=0).
2. Delete an empty account if it's touched
3. An empty account is redefined as either non-existent or empty.
4. Zero value calls and zero value suicides no longer consume the 25k
  reation costs.

params: moved core/config to params

Signed-off-by: Jeffrey Wilcke <jeffrey@ethereum.org>
2016-11-15 12:40:36 +01:00
Jeffrey Wilcke
ef9265d0d7 Merge pull request #3140 from karalabe/release/1.4
Geth 1.4.18: Note 7
2016-10-15 11:38:21 +02:00
Péter Szilágyi
ac0c5dd77f [release/1.4.18] cmd/geth, VERSION: bump version to 1.4.18 2016-10-15 11:02:58 +02:00
Jeffrey Wilcke
9fa42b1e02 [release/1.4.18] core: fixed import reporter
(cherry picked from commit ca419f3cd8)
2016-10-15 11:02:53 +02:00
Felix Lange
0718ebbbce [release/1.4.18] eth: move "timed out DAO fork check, dropping" to debug level
(cherry picked from commit 82b14a05f2)
2016-10-15 11:02:49 +02:00
Felix Lange
843073d453 [release/1.4.18] core: print import stats more often
If geth is busy importing 2048 heavy blocks it can take a while before
it prints anything. This change ensures that a message gets printed
every 8s.

(cherry picked from commit e66b158f0b)
2016-10-15 11:02:43 +02:00
Jeffrey Wilcke
33a6e52aef [release/1.4.18] params: set reprice block number to 2463000
(cherry picked from commit f2be249385)
2016-10-15 11:00:55 +03:00
Felix Lange
18c8ded42c [release/1.4.18] trie: fix regression that linked all downloaded nodes together
The trie sync code links subtries using pointers into node structs.
Since commit 40cdcf1183 nodes are no longer copied when unpacking from
an interface value, causing all nodes to get linked up as the sync
progresses. Fix it by breaking the pointer chain with an explicit copy.

(cherry picked from commit 2cd7a0395d)
2016-10-15 11:00:53 +03:00
Jeffrey Wilcke
00ba748707 [release/1.4.18] core, core/vm: added gas price variance table
This implements 1b & 1c of EIP150 by adding a new GasTable which must be
returned from the RuleSet config method. This table is used to determine
the gas prices for the current epoch.

Please note that when the CreateBySuicide gas price is set it is assumed
that we're in the new epoch phase.

In addition this PR will serve as temporary basis while refactorisation
in being done in the EVM64 PR, which will substentially overhaul the gas
price code.

(cherry picked from commit 64af2aafda)
2016-10-14 19:46:38 +03:00
Felix Lange
8d81eb9999 [release/1.4.18] trie, core/state: improve memory usage and performance (#3135)
* trie: store nodes as pointers

This avoids memory copies when unwrapping node interface values.

name      old time/op  new time/op  delta
Get        388ns ± 8%   215ns ± 2%  -44.56%  (p=0.000 n=15+15)
GetDB      363ns ± 3%   202ns ± 2%  -44.21%  (p=0.000 n=15+15)
UpdateBE  1.57µs ± 2%  1.29µs ± 3%  -17.80%  (p=0.000 n=13+15)
UpdateLE  1.92µs ± 2%  1.61µs ± 2%  -16.25%  (p=0.000 n=14+14)
HashBE    2.16µs ± 6%  2.18µs ± 6%     ~     (p=0.436 n=15+15)
HashLE    7.43µs ± 3%  7.21µs ± 3%   -2.96%  (p=0.000 n=15+13)

* trie: close temporary databases in GetDB benchmark

* trie: don't keep []byte from DB load around

Nodes decoded from a DB load kept hashes and values as sub-slices of
the DB value. This can be a problem because loading from leveldb often
returns []byte with a cap that's larger than necessary, increasing
memory usage.

* trie: unload old cached nodes

* trie, core/state: use cache unloading for account trie

* trie: use explicit private flags (fixes Go 1.5 reflection issue).

* trie: fixup cachegen overflow at request of nick

* core/state: rename journal size constant

(cherry picked from commit 40cdcf1183)
2016-10-14 19:46:36 +03:00
Péter Szilágyi
2425a748ff [release/1.4.18] core: add global (soft) limits on the pending transactions
(cherry picked from commit 182d9cb752)
2016-10-14 19:46:33 +03:00
Nick Johnson
facfe40528 [release/1.4.18] metrics, internal/debug: Add --pprofaddr flag, expose metrics via gexp
(cherry picked from commit 9e9bfc4e26)
2016-10-14 19:46:30 +03:00
Felix Lange
5a6008e004 Merge pull request #3116 from fjl/release/1.4
Backport TxPool limits to release/1.4
2016-10-10 16:19:05 +02:00
Felix Lange
abaa56fea5 [release/1.4.17] cmd/geth, VERSION: bump version to 1.4.17 2016-10-10 16:17:17 +02:00
Felix Lange
a79977bc20 [release/1.4.17] core: lower transaction pool max queue limit
(cherry picked from commit 16d8397e30)
2016-10-10 15:53:01 +02:00
Péter Szilágyi
794d2eb582 [release/1.4.17] core: abstract out a sorted transaction hash map
(cherry picked from commit b4a5251391)
2016-10-10 10:50:02 +02:00
Péter Szilágyi
d7721def26 [release/1.4.17] core: add upper bound on the queued transctions
(cherry picked from commit a183ea29f9)
2016-10-10 10:50:02 +02:00
Péter Szilágyi
ddcf02b119 [release/1.4.17] core/types, miner: switch over to the grouped tx sets
(cherry picked from commit affffb39b3)
2016-10-10 10:49:58 +02:00
Péter Szilágyi
ff697e82dc [release/1.4.17] core, eth, internal, miner: optimize txpool for quick ops
(cherry picked from commit 0ef327bbee)
2016-10-10 10:42:07 +02:00
Péter Szilágyi
df64a9f5ef [release/1.4.17] core, eth, miner: only retain 1 tx/nonce, remove bad ones
(cherry picked from commit 795b70423e)
2016-10-10 10:41:58 +02:00
Felix Lange
4fced0972d [release/1.4.16] cmd/geth, VERSION: bumped version to 1.4.16 2016-10-06 16:25:17 +02:00
Felix Lange
18971d9cb4 [release/1.4.16] core/state: optimize GetState
There is no need to use the reflection-based decoder to decode []byte.

(cherry picked from commit 3c836dd71b)
2016-10-06 16:25:17 +02:00
Felix Lange
74d5251b70 [release/1.4.16] core/state: rename Delete/IsDeleted to Suicide/HasSuicided
The delete/remove naming has caused endless confusion in the past.

(cherry picked from commit 90fce8bfa6)
2016-10-06 16:25:17 +02:00
Felix Lange
46a527d014 [release/1.4.16] core/state: implement reverts by journaling all changes
This commit replaces the deep-copy based state revert mechanism with a
linear complexity journal. This commit also hides several internal
StateDB methods to limit the number of ways in which calling code can
use the journal incorrectly.

As usual consultation and bug fixes to the initial implementation were
provided by @karalabe, @obscuren and @Arachnid. Thank you!

(cherry picked from commit 1f1ea18b54)
2016-10-06 16:25:17 +02:00
Felix Lange
e97b30169b [release/1.4.16] tests: update test files from github.com/ethereum/tests @ 45bc1d21d3c1
Two new tests are skipped because they're buggy. Making some newer
random state tests work required implementing the 'compressed return
value encoding'.

(cherry picked from commit 1b7b2ba216)
2016-10-06 16:25:17 +02:00
Felix Lange
438efdab28 [release/1.4.16] cmd/utils: disable jitvm A/B test 2016-10-06 16:25:17 +02:00
Péter Szilágyi
f7e6fb7d1c [release/1.4.16] eth: monitor malicious header retrieval requests
(cherry picked from commit e482b5694f)
2016-10-06 16:25:17 +02:00
Felix Lange
718881bd35 [release/1.4.16] internal/build: fix git tag env variable for AppVeyor
(cherry picked from commit 82e09c17a9)
2016-10-06 16:25:17 +02:00
Felix Lange
d1bb89d46d [release/1.4.16] crypto/sha3: update from golang.org/x/crypto/sha3 a 80b25ed4
This pulls in the assembly implementation of keccakf1600 for amd64.

(cherry picked from commit 54ea317375)
2016-10-06 16:25:17 +02:00
Péter Szilágyi
1639f1174e Merge pull request #3074 from karalabe/release/1.4
Geth 1.4.15 "Come at me bro"
2016-10-03 11:40:05 +03:00
Péter Szilágyi
d9ed63ec38 [release/1.4.15] VERSION, cmd/geth: bumped version 1.4.15 2016-10-03 11:36:06 +03:00
Felix Lange
ee58202f2f [release/1.4.15] internal/build: use less edgy command to get the branch name
(cherry picked from commit b4b5921dd0)
2016-10-03 11:36:03 +03:00
Felix Lange
9315bc9c3c [release/1.4.15] build: improve debian packaging
This commit tweaks the debian packaging tool:

* All build environment metadata can now be overriden on the command
  line. This allows testing the CI build behaviour locally.
* -unstable packages now actually contain the binaries (oops)
* packages use Go 1.7 to build
* archiving is skipped for PR builds

(cherry picked from commit 4f7627972e)
2016-10-03 11:35:57 +03:00
Péter Szilágyi
945bcb8293 [release/1.4.15] core/state: track dirty state entries for each object
(cherry picked from commit b7159818f9)
2016-10-03 11:24:07 +03:00
Péter Szilágyi
f1949f4d99 [release/1.4.15] cmd, core, internal, light, tests: avoid hashing the code in the VM
(cherry picked from commit cb84e3f029)
2016-10-03 11:24:04 +03:00
Felix Lange
968ab8aa4f [release/1.4.15] trie: fix delete bug for values contained in fullNode
Delete crashed if a fullNode contained a valueNode directly. This bug is
very unlikely to occur with SecureTrie, but can happen with regular
tries. This commit also introduces a randomised test which triggers all
trie operations, which should prevent such bugs in the future.

Credit for finding this bug goes to Github user @rjl493456442.

(cherry picked from commit c3a77d6268)
2016-10-03 11:23:56 +03:00
Péter Szilágyi
f88bca7ba9 Merge pull request #3055 from karalabe/release/1.4
Geth 1.4.14: What else should we rewrite?
2016-09-28 13:40:44 +03:00
Péter Szilágyi
d4608ae0d2 [release/1.4.14] VERSION, cmd/geth: bumped version 1.4.14 2016-09-28 13:32:14 +03:00
Péter Szilágyi
f50c2a5c70 [release/1.4.14] core, eth, trie: reuse trie journals in all our code
(cherry picked from commit 710435b51b)
2016-09-28 13:32:12 +03:00
Felix Lange
ddadf402fc [release/1.4.14] core, trie: replace state caches with trie journal
(cherry picked from commit cd791bd855)
2016-09-28 13:32:10 +03:00
Felix Lange
7c17a6704c [release/1.4.14] build: limit test concurrency
TravisCI and AppVeyor run the tests in very slow VMs.
Some of our tests can't cope with that. Running less tests
in parallel should make them somewhat less flakey.

(cherry picked from commit b0a6b979a3)
2016-09-28 13:32:08 +03:00
Felix Lange
25205d64d7 [release/1.4.14] cmd/utils: don't check for stderr redirect on windows
The redirect check did not work on Go 1.6 and below because Stat
returned an error for stdout and stderr. In Go 1.7 Stat works on stdout
but doesn't return anything meaningful, causing cmd/geth test failures
because the message is printed to stderr only. Fix it by printing to
stdout only.

(cherry picked from commit b04219fdbb)
2016-09-28 13:32:06 +03:00
Felix Lange
03b2f56485 [release/1.4.14] appveyor.yml: don't install Go and MinGW, they're already there (#2813)
(cherry picked from commit e11489eb5f)
2016-09-28 13:31:55 +03:00
Péter Szilágyi
7a5843de31 Merge pull request #3038 from fjl/release/1.4
[release/1.4.13] core/state: track all accounts in canon state
2016-09-26 12:36:53 +03:00
Felix Lange
0cc6397195 [release/1.4.13] node: fix go vet issue 2016-09-26 10:49:40 +02:00
Felix Lange
dc2b23c869 [release/1.4.13] core/state: track all accounts in canon state
This change introduces a global, per-state cache that keeps account data
in the canon state. Thanks to @karalabe for lots of fixes.

(cherry picked from commit a59a93f476)
2016-09-26 10:31:18 +02:00
Péter Szilágyi
6999f1da6b Merge pull request #3036 from karalabe/release/1.4
Geth 1.4.13
2016-09-26 08:55:23 +03:00
Péter Szilágyi
95bfedd599 [release 1.4.12] VERSION, cmd/geth: bumped version 1.4.13 2016-09-26 08:50:13 +03:00
Gustav Simonsson
42e4e18667 [release 1.4.12] core: short-circuit balance change if zero value
(cherry picked from commit 25ed5fedda)
2016-09-26 08:49:59 +03:00
Péter Szilágyi
9bdbaf459a Merge pull request #3008 from karalabe/release/1.4
G
2016-09-19 04:32:32 +03:00
Péter Szilágyi
dfc63c49c7 [release/1.4.12] VERSION, cmd/geth: bumped version 1.4.12 2016-09-19 04:23:42 +03:00
Nick Johnson
e44b2dc881 [release 1.4.12] core/state: Fix memory expansion bug by not copying clean objects
(cherry picked from commit 581b320b9d)
2016-09-19 04:23:36 +03:00
Jeffrey Wilcke
99a0c76435 Merge pull request #2915 from obscuren/release/1.4
Geth 1.4.11
2016-08-18 17:04:31 +02:00
Jeffrey Wilcke
5ca5ccf90c [release/1.4.11] VERSION, cmd/geth: bumped version 1.4.11 2016-08-18 15:25:16 +02:00
Bas van Kervel
c4ed34f008 [release/1.4.11] core: ensure the canonical block is written before the canonical hash is set
(cherry picked from commit bb8059f6aa)

Conflicts:
	core/blockchain.go
	core/database_util.go
	core/headerchain.go
	eth/filters/filter.go
2016-08-18 15:25:16 +02:00
Péter Szilágyi
0ab7e90cbb [release/1.4.11] Godeps: pull in ethash with the big endian build fix
(cherry picked from commit f0134f363b)
2016-08-18 15:25:04 +02:00
Péter Szilágyi
bdbfe572f1 [release/1.4.11] Makefile: support building for the MIPS64 platforms (#2682)
(cherry picked from commit 4c2cc32f2e)
2016-08-18 15:01:51 +02:00
Péter Szilágyi
c4e4baf668 [release/1.4.11] eth/downloader: fewer headers and futures too un ancestor lookup
(cherry picked from commit d68865f3b1)
2016-08-18 15:01:49 +02:00
Péter Szilágyi
86493f9103 [release/1.4.11] eth/downloader: abort sync if master drops (timeout prev)
(cherry picked from commit 8f0a4a25f8)
2016-08-18 15:01:46 +02:00
Péter Szilágyi
6c672a55c0 [release/1.4.11] eth, eth/downloader: don't forward the DAO challenge header
(cherry picked from commit 071af57bcf)
2016-08-18 15:01:43 +02:00
Péter Szilágyi
48709d5340 [release/1.4.11] eth, eth/downloader: better remote head tracking
(cherry picked from commit 1dd272080d)

Conflicts:
	eth/handler.go
	eth/sync.go
2016-08-18 15:01:39 +02:00
Felix Lange
65da8f601f [release/1.4.11] eth, eth/downloader, eth/fetcher: delete eth/61 code
The eth/61 protocol was disabled in #2776, this commit removes its
message handlers and hash-chain sync logic.

(cherry picked from commit 016007bd25)

Conflicts:
	eth/handler.go
	eth/handler_test.go
2016-08-18 15:01:34 +02:00
Péter Szilágyi
2c6214e846 [release/1.4.11] Makefile, build: move cross compilation into ci.go
(cherry picked from commit 8c23f20c68)
2016-08-18 15:01:30 +02:00
Felix Lange
0398075ced [release/1.4.11] build: add ci.go, use it everywhere
The new build script, ci.go, replaces some of the older shell scripts.
ci.go can compile go-ethereum, run the tests, create release archives
and debian source packages.

(cherry picked from commit 6c33ba14a4)
2016-08-18 15:01:27 +02:00
Felix Lange
d1696dbf07 [release/1.4.11] core/vm: hide ecrecover error message
Fixes #2825

(cherry picked from commit e4736fe469)
2016-08-18 15:01:23 +02:00
Felix Lange
626604e86d [release/1.4.11] Godeps: update github.com/rjeczalik/notify to f627deca7a51
Fixes #2829

(cherry picked from commit 4be37222ef)
2016-08-18 15:01:19 +02:00
Péter Szilágyi
9eb2873a9c [release/1.4.11] eth/downloader: fix the stall checks/drops during sync
(cherry picked from commit c7c82f1b44)
2016-08-18 15:01:15 +02:00
Péter Szilágyi
08a7cd74da [release/1.4.11] eth: cancel DAO challenge on peer drop (annoying log)
(cherry picked from commit 91f18ffd47)
2016-08-18 15:01:05 +02:00
Henning Diedrich
35d479b6d3 [release/1.4.11] eth: fix #2710 filter races
and locking bugs found in its wake.

(cherry picked from commit 51f8ce26cf)
2016-08-18 15:00:48 +02:00
1028 changed files with 353613 additions and 69270 deletions

8
.gitignore vendored
View File

@@ -23,17 +23,11 @@ Godeps/_workspace/bin
.project
.settings
deploy/osx/Mist.app
deploy/osx/Mist\ Installer.dmg
cmd/mist/assets/ext/ethereum.js/
# used by the Makefile
/build/_workspace/
/build/bin/
/geth*.zip
# travis
profile.tmp
profile.cov
# vagrant
.vagrant

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "cmd/mist/assets/ext/ethereum.js"]
path = cmd/mist/assets/ext/ethereum.js
url = https://github.com/ethereum/web3.js

View File

@@ -1,31 +1,45 @@
language: go
go:
- 1.4.2
- 1.5.4
- 1.6.2
install:
# - go get code.google.com/p/go.tools/cmd/goimports
# - go get github.com/golang/lint/golint
# - go get golang.org/x/tools/cmd/vet
- go get golang.org/x/tools/cmd/cover
before_script:
# - gofmt -l -w .
# - goimports -l -w .
# - golint .
# - go vet ./...
# - go test -race ./...
script:
- make travis-test-with-coverage
after_success:
- bash <(curl -s https://codecov.io/bash)
env:
global:
- secure: "U2U1AmkU4NJBgKR/uUAebQY87cNL0+1JHjnLOmmXwxYYyj5ralWb1aSuSH3qSXiT93qLBmtaUkuv9fberHVqrbAeVlztVdUsKAq7JMQH+M99iFkC9UiRMqHmtjWJ0ok4COD1sRYixxi21wb/JrMe3M1iL4QJVS61iltjHhVdM64="
go_import_path: github.com/ethereum/go-ethereum
sudo: false
matrix:
include:
- os: linux
dist: trusty
go: 1.4.2
- os: linux
dist: trusty
go: 1.5.4
- os: linux
dist: trusty
go: 1.6.2
- os: osx
go: 1.6.2
# This builder does the PPA upload (and nothing else).
- os: linux
dist: trusty
go: 1.6.2
env: PPA
addons:
apt:
packages:
- devscripts
- debhelper
- dput
script:
- go run build/ci.go debsrc -signer "Felix Lange (Geth CI Testing Key) <fjl@twurst.com>" -upload ppa:lp-fjl/geth-ci-testing
install:
- go get golang.org/x/tools/cmd/cover
script:
- go run build/ci.go install
- go run build/ci.go test -coverage -vet
after_success:
# - go run build/ci.go archive -type tar
notifications:
webhooks:
urls:
- https://webhooks.gitter.im/e/e09ccdce1048c5e03445
on_success: change
on_failure: always
on_start: false

6
Godeps/Godeps.json generated
View File

@@ -20,8 +20,8 @@
},
{
"ImportPath": "github.com/ethereum/ethash",
"Comment": "v23.1-245-g25b32de",
"Rev": "25b32de0c0271065c28c3719c2bfe86959d72f0c"
"Comment": "v23.1-247-g2e80de5",
"Rev": "2e80de5022370cfe632195b1720db52d07ff8a77"
},
{
"ImportPath": "github.com/fatih/color",
@@ -117,7 +117,7 @@
},
{
"ImportPath": "github.com/rjeczalik/notify",
"Rev": "5dd6205716539662f8f14ab513552b41eab69d5d"
"Rev": "f627deca7a510d96f0ef9388f2d0e8b16d21f87f"
},
{
"ImportPath": "github.com/robertkrimen/otto",

0
Godeps/_workspace/src/github.com/ethereum/ethash/setup.py generated vendored Normal file → Executable file
View File

View File

@@ -19,7 +19,7 @@
# define BYTE_ORDER LITTLE_ENDIAN
#elif defined( __QNXNTO__ ) && defined( __BIGENDIAN__ )
# define BIG_ENDIAN 1234
# define BYTE_ORDER BIG_ENDIAN
# define BYTE_ORDER BIG_ENDIAN
#else
# include <endian.h>
#endif
@@ -59,21 +59,20 @@
#define fix_endian32(dst_, src_) dst_ = ethash_swap_u32(src_)
#define fix_endian32_same(val_) val_ = ethash_swap_u32(val_)
#define fix_endian64(dst_, src_) dst_ = ethash_swap_u64(src_
#define fix_endian64(dst_, src_) dst_ = ethash_swap_u64(src_)
#define fix_endian64_same(val_) val_ = ethash_swap_u64(val_)
#define fix_endian_arr32(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
arr_[i_] = ethash_swap_u32(arr_[i_]); \
} \
while (0)
#define fix_endian_arr64(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_), ++i_) { \
arr_[i_] = ethash_swap_u64(arr_[i_]); \
} \
while (0) \
#define fix_endian_arr32(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_); ++i_) { \
arr_[i_] = ethash_swap_u32(arr_[i_]); \
} \
} while (0)
#define fix_endian_arr64(arr_, size_) \
do { \
for (unsigned i_ = 0; i_ < (size_); ++i_) { \
arr_[i_] = ethash_swap_u64(arr_[i_]); \
} \
} while (0)
#else
# error "endian not supported"
#endif // BYTE_ORDER

View File

@@ -257,7 +257,7 @@ static bool ethash_hash(
void ethash_quick_hash(
ethash_h256_t* return_hash,
ethash_h256_t const* header_hash,
uint64_t const nonce,
uint64_t nonce,
ethash_h256_t const* mix_hash
)
{

View File

@@ -21,10 +21,9 @@ env:
- PATH=$HOME/bin:$PATH
install:
- go get golang.org/x/tools/cmd/vet
- go get -t -v ./...
script:
- go tool vet -all .
- "(go version | grep -q 1.4) || go tool vet -all ."
- go install $GOFLAGS ./...
- go test -v -race $GOFLAGS ./...

View File

@@ -11,7 +11,6 @@ environment:
install:
- go version
- go get golang.org/x/tools/cmd/vet
- go get -v -t ./...
build_script:

View File

@@ -133,7 +133,7 @@ func (w *watch) Dispatch(ev []FSEvent) {
ev[i].Flags, ev[i].Path, i, ev[i].ID, len(ev))
if ev[i].Flags&failure != 0 {
// TODO(rjeczalik): missing error handling
panic("unhandled error: " + Event(ev[i].Flags).String())
continue
}
if !strings.HasPrefix(ev[i].Path, w.path) {
continue

105
Makefile
View File

@@ -2,8 +2,8 @@
# with Go source code. If you know what GOPATH is then you probably
# don't need to bother with make.
.PHONY: geth geth-cross evm all test travis-test-with-coverage xgo clean
.PHONY: geth-linux geth-linux-386 geth-linux-amd64
.PHONY: geth geth-cross evm all test clean
.PHONY: geth-linux geth-linux-386 geth-linux-amd64 geth-linux-mips64 geth-linux-mips64le
.PHONY: geth-linux-arm geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64
.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
.PHONY: geth-windows geth-windows-386 geth-windows-amd64
@@ -13,25 +13,41 @@ GOBIN = build/bin
GO ?= latest
geth:
build/env.sh go build -i -v $(shell build/flags.sh) -o $(GOBIN)/geth ./cmd/geth
build/env.sh go run build/ci.go install ./cmd/geth
@echo "Done building."
@echo "Run \"$(GOBIN)/geth\" to launch geth."
evm:
build/env.sh go run build/ci.go install ./cmd/evm
@echo "Done building."
@echo "Run \"$(GOBIN)/evm to start the evm."
all:
build/env.sh go run build/ci.go install
test: all
build/env.sh go run build/ci.go test
clean:
rm -fr build/_workspace/pkg/ Godeps/_workspace/pkg $(GOBIN)/*
# Cross Compilation Targets (xgo)
geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios
@echo "Full cross compilation done:"
@ls -ld $(GOBIN)/geth-*
geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm
geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm geth-linux-mips64 geth-linux-mips64le
@echo "Linux cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-*
geth-linux-386: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v $(shell build/flags.sh) ./cmd/geth
geth-linux-386:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/geth
@echo "Linux 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep 386
geth-linux-amd64: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v $(shell build/flags.sh) ./cmd/geth
geth-linux-amd64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/geth
@echo "Linux amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep amd64
@@ -39,37 +55,47 @@ geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-ar
@echo "Linux ARM cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm
geth-linux-arm-5: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v $(shell build/flags.sh) ./cmd/geth
geth-linux-arm-5:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/geth
@echo "Linux ARMv5 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-5
geth-linux-arm-6: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v $(shell build/flags.sh) ./cmd/geth
geth-linux-arm-6:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/geth
@echo "Linux ARMv6 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-6
geth-linux-arm-7: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v $(shell build/flags.sh) ./cmd/geth
geth-linux-arm-7:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/geth
@echo "Linux ARMv7 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm-7
geth-linux-arm64: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v $(shell build/flags.sh) ./cmd/geth
geth-linux-arm64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/geth
@echo "Linux ARM64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep arm64
geth-linux-mips64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64 -v ./cmd/geth
@echo "Linux MIPS64 cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mips64
geth-linux-mips64le:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64le -v ./cmd/geth
@echo "Linux MIPS64le cross compilation done:"
@ls -ld $(GOBIN)/geth-linux-* | grep mips64le
geth-darwin: geth-darwin-386 geth-darwin-amd64
@echo "Darwin cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-*
geth-darwin-386: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v $(shell build/flags.sh) ./cmd/geth
geth-darwin-386:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/geth
@echo "Darwin 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-* | grep 386
geth-darwin-amd64: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v $(shell build/flags.sh) ./cmd/geth
geth-darwin-amd64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/geth
@echo "Darwin amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-darwin-* | grep amd64
@@ -77,45 +103,22 @@ geth-windows: geth-windows-386 geth-windows-amd64
@echo "Windows cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-*
geth-windows-386: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v $(shell build/flags.sh) ./cmd/geth
geth-windows-386:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/geth
@echo "Windows 386 cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-* | grep 386
geth-windows-amd64: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v $(shell build/flags.sh) ./cmd/geth
geth-windows-amd64:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/geth
@echo "Windows amd64 cross compilation done:"
@ls -ld $(GOBIN)/geth-windows-* | grep amd64
geth-android: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v $(shell build/flags.sh) ./cmd/geth
geth-android:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v ./cmd/geth
@echo "Android cross compilation done:"
@ls -ld $(GOBIN)/geth-android-*
geth-ios: xgo
build/env.sh $(GOBIN)/xgo --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v $(shell build/flags.sh) ./cmd/geth
geth-ios:
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v ./cmd/geth
@echo "iOS framework cross compilation done:"
@ls -ld $(GOBIN)/geth-ios-*
evm:
build/env.sh $(GOROOT)/bin/go install -v $(shell build/flags.sh) ./cmd/evm
@echo "Done building."
@echo "Run \"$(GOBIN)/evm to start the evm."
all:
for cmd in `ls ./cmd/`; do \
build/env.sh go build -i -v $(shell build/flags.sh) -o $(GOBIN)/$$cmd ./cmd/$$cmd; \
done
test: all
build/env.sh go test ./...
travis-test-with-coverage: all
build/env.sh go vet ./...
build/env.sh build/test-global-coverage.sh
xgo:
build/env.sh go get github.com/karalabe/xgo
clean:
rm -fr build/_workspace/pkg/ Godeps/_workspace/pkg $(GOBIN)/*

View File

@@ -1 +1 @@
1.4.10
1.4.19

View File

@@ -27,10 +27,11 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
)
// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier)
var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)}
var chainConfig = &params.ChainConfig{ChainId: new(big.Int), HomesteadBlock: big.NewInt(0), EIP150Block: new(big.Int), EIP158Block: new(big.Int)}
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
@@ -72,8 +73,7 @@ func (b *SimulatedBackend) Commit() {
// Rollback aborts all pending transactions, reverting to the last committed state.
func (b *SimulatedBackend) Rollback() {
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
b.pendingBlock = blocks[0]
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
}
@@ -97,7 +97,8 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
statedb *state.StateDB
)
if pending {
block, statedb = b.pendingBlock, b.pendingState.Copy()
block, statedb = b.pendingBlock, b.pendingState
defer statedb.RevertToSnapshot(statedb.Snapshot())
} else {
block = b.blockchain.CurrentBlock()
statedb, _ = b.blockchain.State()
@@ -119,6 +120,7 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
value: new(big.Int),
data: data,
}
// Execute the call and return
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
gaspool := new(core.GasPool).AddGas(common.MaxBig)
@@ -146,8 +148,10 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
// Create a copy of the currently pending state db to screw around with
var (
block = b.pendingBlock
statedb = b.pendingState.Copy()
statedb = b.pendingState
)
defer statedb.RevertToSnapshot(statedb.Snapshot())
// If there's no code to interact with, respond with an appropriate error
if contract != nil {
if code := statedb.GetCode(*contract); len(code) == 0 {
@@ -178,7 +182,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
// transaction injection to the remote node.
func (b *SimulatedBackend) SendTransaction(tx *types.Transaction) error {
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
for _, tx := range b.pendingBlock.Transactions() {
block.AddTx(tx)
}

33
appveyor.yml Normal file
View File

@@ -0,0 +1,33 @@
os: Visual Studio 2015
# Clone directly into GOPATH.
clone_folder: c:\gopath\src\github.com\ethereum\go-ethereum
clone_depth: 5
version: "{branch}.{build}"
environment:
global:
# Go stuff
GOPATH: c:\gopath
GO: c:\go\bin\go
GOROOT: c:\go
CC: C:\msys64\mingw64\bin\gcc.exe
# MSYS2 stuff
MSYS2_ARCH: x86_64
MSYSTEM: MINGW64
PATH: C:\msys64\mingw64\bin\;%PATH%
install:
- "%GO% version"
- "%CC% --version"
build_script:
- "%GO% run build\\ci.go install"
test_script:
- "%GO% run build\\ci.go test -vet -coverage"
after_build:
- "%GO% run build\\ci.go archive -type zip"
artifacts:
- path: geth-*.zip

49
build/ci-notes.md Normal file
View File

@@ -0,0 +1,49 @@
# Debian Packaging
Tagged releases and develop branch commits are available as installable Debian packages
for Ubuntu. Packages are built for the all Ubuntu versions which are supported by
Canonical:
- Trusty Tahr (14.04 LTS)
- Wily Werewolf (15.10)
- Xenial Xerus (16.04 LTS)
- Yakkety Yak (16.10)
Packages of develop branch commits have suffix -unstable and cannot be installed alongside
the stable version. Switching between release streams requires user intervention.
The packages are built and served by launchpad.net. We generate a Debian source package
for each distribution and upload it. Their builder picks up the source package, builds it
and installs the new version into the PPA repository. Launchpad requires a valid signature
by a team member for source package uploads. The signing key is stored in an environment
variable which Travis CI makes available to certain builds.
We want to build go-ethereum with the most recent version of Go, irrespective of the Go
version that is available in the main Ubuntu repository. In order to make this possible,
our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on
golang-1.7, which is co-installable alongside the regular golang package. PPA dependencies
can be edited at https://launchpad.net/%7Elp-fjl/+archive/ubuntu/geth-ci-testing/+edit-dependencies
## Building Packages Locally (for testing)
You need to run Ubuntu to do test packaging.
Add the gophers PPA and install Go 1.7 and Debian packaging tools:
$ sudo apt-add-repository ppa:gophers/ubuntu/archive
$ sudo apt-get update
$ sudo apt-get install build-essential golang-1.7 devscripts debhelper
Create the source packages:
$ go run build/ci.go debsrc -workdir dist
Then go into the source package directory for your running distribution and build the package:
$ cd dist/ethereum-unstable-1.5.0+xenial
$ dpkg-buildpackage
Built packages are placed in the dist/ directory.
$ cd ..
$ dpkg-deb -c geth-unstable_1.5.0+xenial_amd64.deb

483
build/ci.go Normal file
View File

@@ -0,0 +1,483 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// +build none
/*
The ci command is called from Continuous Integration scripts.
Usage: go run ci.go <command> <command flags/arguments>
Available commands are:
install [ packages... ] -- builds packages and executables
test [ -coverage ] [ -vet ] [ packages... ] -- runs the tests
archive [ -type zip|tar ] -- archives build artefacts
importkeys -- imports signing keys from env
debsrc [ -sign key-id ] [ -upload dest ] -- creates a debian source package
xgo [ options ] -- cross builds according to options
For all commands, -n prevents execution of external programs (dry run mode).
*/
package main
import (
"bytes"
"encoding/base64"
"flag"
"fmt"
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"time"
"../internal/build"
)
var (
// Files that end up in the geth*.zip archive.
gethArchiveFiles = []string{
"COPYING",
executablePath("geth"),
}
// Files that end up in the geth-alltools*.zip archive.
allToolsArchiveFiles = []string{
"COPYING",
executablePath("abigen"),
executablePath("evm"),
executablePath("geth"),
executablePath("rlpdump"),
}
// A debian package is created for all executables listed here.
debExecutables = []debExecutable{
{
Name: "geth",
Description: "Ethereum CLI client.",
},
{
Name: "rlpdump",
Description: "Developer utility tool that prints RLP structures.",
},
{
Name: "evm",
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
},
{
Name: "abigen",
Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.",
},
}
// Distros for which packages are created.
// Note: vivid is unsupported because there is no golang-1.6 package for it.
debDistros = []string{"trusty", "wily", "xenial", "yakkety"}
)
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
func executablePath(name string) string {
if runtime.GOOS == "windows" {
name += ".exe"
}
return filepath.Join(GOBIN, name)
}
func main() {
log.SetFlags(log.Lshortfile)
if _, err := os.Stat(filepath.Join("build", "ci.go")); os.IsNotExist(err) {
log.Fatal("this script must be run from the root of the repository")
}
if len(os.Args) < 2 {
log.Fatal("need subcommand as first argument")
}
switch os.Args[1] {
case "install":
doInstall(os.Args[2:])
case "test":
doTest(os.Args[2:])
case "archive":
doArchive(os.Args[2:])
case "debsrc":
doDebianSource(os.Args[2:])
case "xgo":
doXgo(os.Args[2:])
default:
log.Fatal("unknown command ", os.Args[1])
}
}
// Compiling
func doInstall(cmdline []string) {
flag.CommandLine.Parse(cmdline)
env := build.Env()
// Check Go version. People regularly open issues about compilation
// failure with outdated Go. This should save them the trouble.
if runtime.Version() < "go1.4" && !strings.HasPrefix(runtime.Version(), "devel") {
log.Println("You have Go version", runtime.Version())
log.Println("go-ethereum requires at least Go version 1.4 and cannot")
log.Println("be compiled with an earlier version. Please upgrade your Go installation.")
os.Exit(1)
}
// Compile packages given as arguments, or everything if there are no arguments.
packages := []string{"./..."}
if flag.NArg() > 0 {
packages = flag.Args()
}
goinstall := goTool("install", buildFlags(env)...)
goinstall.Args = append(goinstall.Args, "-v")
goinstall.Args = append(goinstall.Args, packages...)
build.MustRun(goinstall)
}
func buildFlags(env build.Environment) (flags []string) {
if os.Getenv("GO_OPENCL") != "" {
flags = append(flags, "-tags", "opencl")
}
// Since Go 1.5, the separator char for link time assignments
// is '=' and using ' ' prints a warning. However, Go < 1.5 does
// not support using '='.
sep := " "
if runtime.Version() > "go1.5" || strings.Contains(runtime.Version(), "devel") {
sep = "="
}
// Set gitCommit constant via link-time assignment.
if env.Commit != "" {
flags = append(flags, "-ldflags", "-X main.gitCommit"+sep+env.Commit)
}
return flags
}
func goTool(subcmd string, args ...string) *exec.Cmd {
gocmd := filepath.Join(runtime.GOROOT(), "bin", "go")
cmd := exec.Command(gocmd, subcmd)
cmd.Args = append(cmd.Args, args...)
cmd.Env = []string{
"GOPATH=" + build.GOPATH(),
"GOBIN=" + GOBIN,
}
for _, e := range os.Environ() {
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") {
continue
}
cmd.Env = append(cmd.Env, e)
}
return cmd
}
// Running The Tests
//
// "tests" also includes static analysis tools such as vet.
func doTest(cmdline []string) {
var (
vet = flag.Bool("vet", false, "Whether to run go vet")
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
)
flag.CommandLine.Parse(cmdline)
packages := []string{"./..."}
if len(flag.CommandLine.Args()) > 0 {
packages = flag.CommandLine.Args()
}
// Run analysis tools before the tests.
if *vet {
build.MustRun(goTool("vet", packages...))
}
// Run the actual tests.
gotest := goTool("test")
// Test a single package at a time. CI builders are slow
// and some tests run into timeouts under load.
gotest.Args = append(gotest.Args, "-p", "1")
if *coverage {
gotest.Args = append(gotest.Args, "-covermode=atomic", "-cover")
}
gotest.Args = append(gotest.Args, packages...)
build.MustRun(gotest)
}
// Release Packaging
func doArchive(cmdline []string) {
var (
atype = flag.String("type", "zip", "Type of archive to write (zip|tar)")
ext string
)
flag.CommandLine.Parse(cmdline)
switch *atype {
case "zip":
ext = ".zip"
case "tar":
ext = ".tar.gz"
default:
log.Fatal("unknown archive type: ", atype)
}
env := build.Env()
maybeSkipArchive(env)
base := archiveBasename(env)
if err := build.WriteArchive("geth-"+base, ext, gethArchiveFiles); err != nil {
log.Fatal(err)
}
if err := build.WriteArchive("geth-alltools-"+base, ext, allToolsArchiveFiles); err != nil {
log.Fatal(err)
}
}
func archiveBasename(env build.Environment) string {
// date := time.Now().UTC().Format("200601021504")
platform := runtime.GOOS + "-" + runtime.GOARCH
archive := platform + "-" + build.VERSION()
if env.Commit != "" {
archive += "-" + env.Commit[:8]
}
return archive
}
// skips archiving for some build configurations.
func maybeSkipArchive(env build.Environment) {
if env.IsPullRequest {
log.Printf("skipping because this is a PR build")
os.Exit(0)
}
if env.Branch != "develop" && !strings.HasPrefix(env.Tag, "v1.") {
log.Printf("skipping because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag)
os.Exit(0)
}
}
// Debian Packaging
func doDebianSource(cmdline []string) {
var (
signer = flag.String("signer", "", `Signing key name, also used as package author`)
upload = flag.String("upload", "", `Where to upload the source package (usually "ppa:ethereum/ethereum")`)
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
now = time.Now()
)
flag.CommandLine.Parse(cmdline)
*workdir = makeWorkdir(*workdir)
env := build.Env()
maybeSkipArchive(env)
// Import the signing key.
if b64key := os.Getenv("PPA_SIGNING_KEY"); b64key != "" {
key, err := base64.StdEncoding.DecodeString(b64key)
if err != nil {
log.Fatal("invalid base64 PPA_SIGNING_KEY")
}
gpg := exec.Command("gpg", "--import")
gpg.Stdin = bytes.NewReader(key)
build.MustRun(gpg)
}
// Create the packages.
for _, distro := range debDistros {
meta := newDebMetadata(distro, *signer, env, now)
pkgdir := stageDebianSource(*workdir, meta)
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc")
debuild.Dir = pkgdir
build.MustRun(debuild)
changes := fmt.Sprintf("%s_%s_source.changes", meta.Name(), meta.VersionString())
changes = filepath.Join(*workdir, changes)
if *signer != "" {
build.MustRunCommand("debsign", changes)
}
if *upload != "" {
build.MustRunCommand("dput", *upload, changes)
}
}
}
func makeWorkdir(wdflag string) string {
var err error
if wdflag != "" {
err = os.MkdirAll(wdflag, 0744)
} else {
wdflag, err = ioutil.TempDir("", "eth-deb-build-")
}
if err != nil {
log.Fatal(err)
}
return wdflag
}
func isUnstableBuild(env build.Environment) bool {
if env.Branch != "develop" && env.Tag != "" {
return false
}
return true
}
type debMetadata struct {
Env build.Environment
// go-ethereum version being built. Note that this
// is not the debian package version. The package version
// is constructed by VersionString.
Version string
Author string // "name <email>", also selects signing key
Distro, Time string
Executables []debExecutable
}
type debExecutable struct {
Name, Description string
}
func newDebMetadata(distro, author string, env build.Environment, t time.Time) debMetadata {
if author == "" {
// No signing key, use default author.
author = "Ethereum Builds <fjl@ethereum.org>"
}
return debMetadata{
Env: env,
Author: author,
Distro: distro,
Version: build.VERSION(),
Time: t.Format(time.RFC1123Z),
Executables: debExecutables,
}
}
// Name returns the name of the metapackage that depends
// on all executable packages.
func (meta debMetadata) Name() string {
if isUnstableBuild(meta.Env) {
return "ethereum-unstable"
}
return "ethereum"
}
// VersionString returns the debian version of the packages.
func (meta debMetadata) VersionString() string {
vsn := meta.Version
if meta.Env.Buildnum != "" {
vsn += "+build" + meta.Env.Buildnum
}
if meta.Distro != "" {
vsn += "+" + meta.Distro
}
return vsn
}
// ExeList returns the list of all executable packages.
func (meta debMetadata) ExeList() string {
names := make([]string, len(meta.Executables))
for i, e := range meta.Executables {
names[i] = meta.ExeName(e)
}
return strings.Join(names, ", ")
}
// ExeName returns the package name of an executable package.
func (meta debMetadata) ExeName(exe debExecutable) string {
if isUnstableBuild(meta.Env) {
return exe.Name + "-unstable"
}
return exe.Name
}
// ExeConflicts returns the content of the Conflicts field
// for executable packages.
func (meta debMetadata) ExeConflicts(exe debExecutable) string {
if isUnstableBuild(meta.Env) {
// Set up the conflicts list so that the *-unstable packages
// cannot be installed alongside the regular version.
//
// https://www.debian.org/doc/debian-policy/ch-relationships.html
// is very explicit about Conflicts: and says that Breaks: should
// be preferred and the conflicting files should be handled via
// alternates. We might do this eventually but using a conflict is
// easier now.
return "ethereum, " + exe.Name
}
return ""
}
func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) {
pkg := meta.Name() + "-" + meta.VersionString()
pkgdir = filepath.Join(tmpdir, pkg)
if err := os.Mkdir(pkgdir, 0755); err != nil {
log.Fatal(err)
}
// Copy the source code.
build.MustRunCommand("git", "checkout-index", "-a", "--prefix", pkgdir+string(filepath.Separator))
// Put the debian build files in place.
debian := filepath.Join(pkgdir, "debian")
build.Render("build/deb.rules", filepath.Join(debian, "rules"), 0755, meta)
build.Render("build/deb.changelog", filepath.Join(debian, "changelog"), 0644, meta)
build.Render("build/deb.control", filepath.Join(debian, "control"), 0644, meta)
build.Render("build/deb.copyright", filepath.Join(debian, "copyright"), 0644, meta)
build.RenderString("8\n", filepath.Join(debian, "compat"), 0644, meta)
build.RenderString("3.0 (native)\n", filepath.Join(debian, "source/format"), 0644, meta)
for _, exe := range meta.Executables {
install := filepath.Join(debian, meta.ExeName(exe)+".install")
docs := filepath.Join(debian, meta.ExeName(exe)+".docs")
build.Render("build/deb.install", install, 0644, exe)
build.Render("build/deb.docs", docs, 0644, exe)
}
return pkgdir
}
// Cross compilation
func doXgo(cmdline []string) {
flag.CommandLine.Parse(cmdline)
env := build.Env()
// Make sure xgo is available for cross compilation
gogetxgo := goTool("get", "github.com/karalabe/xgo")
build.MustRun(gogetxgo)
// Execute the actual cross compilation
xgo := xgoTool(append(buildFlags(env), flag.Args()...))
build.MustRun(xgo)
}
func xgoTool(args []string) *exec.Cmd {
cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
cmd.Env = []string{
"GOPATH=" + build.GOPATH(),
"GOBIN=" + GOBIN,
}
for _, e := range os.Environ() {
if strings.HasPrefix(e, "GOPATH=") || strings.HasPrefix(e, "GOBIN=") {
continue
}
cmd.Env = append(cmd.Env, e)
}
return cmd
}

5
build/deb.changelog Normal file
View File

@@ -0,0 +1,5 @@
{{.Name}} ({{.VersionString}}) {{.Distro}}; urgency=low
* git build of {{.Env.Commit}}
-- {{.Author}} {{.Time}}

25
build/deb.control Normal file
View File

@@ -0,0 +1,25 @@
Source: {{.Name}}
Section: science
Priority: extra
Maintainer: {{.Author}}
Build-Depends: debhelper (>= 8.0.0), golang-1.7
Standards-Version: 3.9.5
Homepage: https://ethereum.org
Vcs-Git: git://github.com/ethereum/go-ethereum.git
Vcs-Browser: https://github.com/ethereum/go-ethereum
Package: {{.Name}}
Architecture: any
Depends: ${misc:Depends}, {{.ExeList}}
Description: Meta-package to install geth and other tools
Meta-package to install geth and other tools
{{range .Executables}}
Package: {{$.ExeName .}}
Conflicts: {{$.ExeConflicts .}}
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Built-Using: ${misc:Built-Using}
Description: {{.Description}}
{{.Description}}
{{end}}

14
build/deb.copyright Normal file
View File

@@ -0,0 +1,14 @@
Copyright 2016 The go-ethereum Authors
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/>.

1
build/deb.docs Normal file
View File

@@ -0,0 +1 @@
AUTHORS

1
build/deb.install Normal file
View File

@@ -0,0 +1 @@
build/bin/{{.Name}} usr/bin

13
build/deb.rules Normal file
View File

@@ -0,0 +1,13 @@
#!/usr/bin/make -f
# -*- makefile -*-
# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
override_dh_auto_build:
build/env.sh /usr/lib/go-1.7/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
override_dh_auto_test:
%:
dh $@

View File

@@ -20,9 +20,8 @@ fi
# Set up the environment to use the workspace.
# Also add Godeps workspace so we build using canned dependencies.
GOPATH="$ethdir/go-ethereum/Godeps/_workspace:$workspace"
GOBIN="$PWD/build/bin"
export GOPATH GOBIN
GOPATH="$workspace"
export GOPATH
# Run the command inside the workspace.
cd "$ethdir/go-ethereum"

View File

@@ -1,22 +0,0 @@
#!/bin/sh
set -e
if [ ! -f "build/env.sh" ]; then
echo "$0 must be run from the root of the repository."
exit 2
fi
# Since Go 1.5, the separator char for link time assignments
# is '=' and using ' ' prints a warning. However, Go < 1.5 does
# not support using '='.
sep=$(go version | awk '{ if ($3 >= "go1.5" || index($3, "devel")) print "="; else print " "; }' -)
# set gitCommit when running from a Git checkout.
if [ -f ".git/HEAD" ]; then
echo "-ldflags '-X main.gitCommit$sep$(git rev-parse HEAD)'"
fi
if [ ! -z "$GO_OPENCL" ]; then
echo "-tags opencl"
fi

View File

@@ -1,15 +0,0 @@
#!/usr/bin/env bash
set -e
echo "" > coverage.txt
for d in $(find ./* -maxdepth 10 -type d -not -path "./build" -not -path "./Godeps/*" ); do
if ls $d/*.go &> /dev/null; then
go test -coverprofile=profile.out -covermode=atomic $d
if [ -f profile.out ]; then
cat profile.out >> coverage.txt
echo '<<<<<< EOF' >> coverage.txt
rm profile.out
fi
fi
done

View File

@@ -49,7 +49,6 @@ var (
// don't relicense vendored sources
"crypto/sha3/", "crypto/ecies/", "logger/glog/",
"crypto/secp256k1/curve.go",
"trie/arc.go",
}
// paths with this prefix are licensed as GPL. all other files are LGPL.

View File

@@ -1,26 +0,0 @@
@echo off
if not exist .\build\win-ci-compile.bat (
echo This script must be run from the root of the repository.
exit /b
)
if not defined GOPATH (
echo GOPATH is not set.
exit /b
)
set GOPATH=%GOPATH%;%cd%\Godeps\_workspace
set GOBIN=%cd%\build\bin
rem set gitCommit when running from a Git checkout.
set goLinkFlags=""
if exist ".git\HEAD" (
where /q git
if not errorlevel 1 (
for /f %%h in ('git rev-parse HEAD') do (
set goLinkFlags="-X main.gitCommit=%%h"
)
)
)
@echo on
go install -v -ldflags %goLinkFlags% ./...

View File

@@ -1,15 +0,0 @@
@echo off
if not exist .\build\win-ci-test.bat (
echo This script must be run from the root of the repository.
exit /b
)
if not defined GOPATH (
echo GOPATH is not set.
exit /b
)
set GOPATH=%GOPATH%;%cd%\Godeps\_workspace
set GOBIN=%cd%\build\bin
@echo on
go test ./...

View File

@@ -74,12 +74,13 @@ func runTestWithReader(test string, r io.Reader) error {
var err error
switch strings.ToLower(test) {
case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, r, skipTests)
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, params.MainNetHomesteadGasRepriceBlock, r, skipTests)
case "st", "state", "statetest", "statetests":
rs := tests.RuleSet{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true}
rs := &params.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true, EIP150Block: params.MainNetHomesteadGasRepriceBlock}
err = tests.RunStateTestWithReader(rs, r, skipTests)
case "tx", "transactiontest", "transactiontests":
err = tests.RunTransactionTestsWithReader(r, skipTests)
rs := &params.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true, EIP150Block: params.MainNetHomesteadGasRepriceBlock}
err = tests.RunTransactionTestsWithReader(rs, r, skipTests)
case "vm", "vmtest", "vmtests":
err = tests.RunVmTestWithReader(r, skipTests)
case "rlp", "rlptest", "rlptests":

View File

@@ -30,8 +30,10 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"gopkg.in/urfave/cli.v1"
)
@@ -141,7 +143,9 @@ func run(ctx *cli.Context) error {
)
} else {
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
code := common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))
receiver.SetCode(crypto.Keccak256Hash(code), code)
ret, err = vmenv.Call(
sender,
receiver.Address(),
@@ -154,7 +158,7 @@ func run(ctx *cli.Context) error {
vmdone := time.Since(tstart)
if ctx.GlobalBool(DumpFlag.Name) {
statedb.Commit()
statedb.Commit(true)
fmt.Println(string(statedb.Dump()))
}
vm.StdErrFormat(vmenv.StructLogs())
@@ -215,27 +219,30 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg
return env
}
// ruleSet implements vm.RuleSet and will always default to the homestead rule set.
// ruleSet implements vm.ChainConfig and will always default to the homestead rule set.
type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true }
func (ruleSet) GasTable(*big.Int) params.GasTable {
return params.GasTableHomesteadGasRepriceFork
}
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() }
func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) }
func (self *VMEnv) Origin() common.Address { return *self.transactor }
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
func (self *VMEnv) Time() *big.Int { return self.time }
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
func (self *VMEnv) Depth() int { return 0 }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig }
func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() }
func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) }
func (self *VMEnv) Origin() common.Address { return *self.transactor }
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
func (self *VMEnv) Time() *big.Int { return self.time }
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
func (self *VMEnv) Value() *big.Int { return self.value }
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
func (self *VMEnv) Depth() int { return 0 }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) GetHash(n uint64) common.Hash {
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
return self.block.Hash()

View File

@@ -50,7 +50,7 @@ const (
clientIdentifier = "Geth" // Client identifier to advertise over the network
versionMajor = 1 // Major version component of the current release
versionMinor = 4 // Minor version component of the current release
versionPatch = 10 // Patch version component of the current release
versionPatch = 19 // Patch version component of the current release
versionMeta = "stable" // Version metadata to append to the version string
versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle

View File

@@ -26,7 +26,6 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
@@ -130,8 +129,7 @@ func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node
ethConf := &eth.Config{
TestGenesisState: db,
TestGenesisBlock: test.Genesis,
ChainConfig: &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock},
AccountManager: accman,
ChainConfig: &params.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock},
}
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil {
return nil, err

View File

@@ -23,6 +23,7 @@ import (
"os"
"os/signal"
"regexp"
"runtime"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
@@ -52,10 +53,16 @@ func openLogFile(Datadir string, filename string) *os.File {
// is redirected to a different file.
func Fatalf(format string, args ...interface{}) {
w := io.MultiWriter(os.Stdout, os.Stderr)
outf, _ := os.Stdout.Stat()
errf, _ := os.Stderr.Stat()
if outf != nil && errf != nil && os.SameFile(outf, errf) {
w = os.Stderr
if runtime.GOOS == "windows" {
// The SameFile check below doesn't work on Windows.
// stdout is unlikely to get redirected though, so just print there.
w = os.Stdout
} else {
outf, _ := os.Stdout.Stat()
errf, _ := os.Stderr.Stat()
if outf != nil && errf != nil && os.SameFile(outf, errf) {
w = os.Stderr
}
}
fmt.Fprintf(w, "Fatal: "+format+"\n", args...)
logger.Flush()

View File

@@ -22,13 +22,11 @@ import (
"io/ioutil"
"math"
"math/big"
"math/rand"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
"time"
"github.com/ethereum/ethash"
"github.com/ethereum/go-ethereum/accounts"
@@ -667,17 +665,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
}
// Configure the Ethereum service
accman := MakeAccountManager(ctx)
// initialise new random number generator
rand := rand.New(rand.NewSource(time.Now().UnixNano()))
// get enabled jit flag
jitEnabled := ctx.GlobalBool(VMEnableJitFlag.Name)
// if the jit is not enabled enable it for 10 pct of the people
if !jitEnabled && rand.Float64() < 0.1 {
jitEnabled = true
glog.V(logger.Info).Infoln("You're one of the lucky few that will try out the JIT VM (random). If you get a consensus failure please be so kind to report this incident with the block hash that failed. You can switch to the regular VM by setting --jitvm=false")
}
ethConf := &eth.Config{
ChainConfig: MustMakeChainConfig(ctx),
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
@@ -781,7 +769,7 @@ func SetupNetwork(ctx *cli.Context) {
}
// MustMakeChainConfig reads the chain configuration from the database in ctx.Datadir.
func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig {
func MustMakeChainConfig(ctx *cli.Context) *params.ChainConfig {
db := MakeChainDatabase(ctx)
defer db.Close()
@@ -789,9 +777,9 @@ func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig {
}
// MustMakeChainConfigFromDb reads the chain configuration from the given database.
func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig {
func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainConfig {
// If the chain is already initialized, use any existing chain configs
config := new(core.ChainConfig)
config := new(params.ChainConfig)
genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0))
if genesis != nil {
@@ -805,6 +793,9 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC
Fatalf("Could not make chain configuration: %v", err)
}
}
if config.ChainId == nil {
config.ChainId = new(big.Int)
}
// Set any missing fields due to them being unset or system upgrade
if config.HomesteadBlock == nil {
if ctx.GlobalBool(TestNetFlag.Name) {
@@ -821,6 +812,41 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC
}
config.DAOForkSupport = true
}
if config.EIP150Block == nil {
if ctx.GlobalBool(TestNetFlag.Name) {
config.EIP150Block = params.TestNetHomesteadGasRepriceBlock
} else {
config.EIP150Block = params.MainNetHomesteadGasRepriceBlock
}
}
if config.EIP150Hash == (common.Hash{}) {
if ctx.GlobalBool(TestNetFlag.Name) {
config.EIP150Hash = params.TestNetHomesteadGasRepriceHash
} else {
config.EIP150Hash = params.MainNetHomesteadGasRepriceHash
}
}
if config.EIP155Block == nil {
if ctx.GlobalBool(TestNetFlag.Name) {
config.EIP150Block = params.TestNetSpuriousDragon
} else {
config.EIP155Block = params.MainNetSpuriousDragon
}
}
if config.EIP158Block == nil {
if ctx.GlobalBool(TestNetFlag.Name) {
config.EIP158Block = params.TestNetSpuriousDragon
} else {
config.EIP158Block = params.MainNetSpuriousDragon
}
}
if config.ChainId.BitLen() == 0 {
if ctx.GlobalBool(TestNetFlag.Name) {
config.ChainId = params.TestNetChainID
} else {
config.ChainId = params.MainNetChainID
}
}
// Force override any existing configs if explicitly requested
switch {
case ctx.GlobalBool(SupportDAOFork.Name):

View File

@@ -32,11 +32,12 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
)
// registryAPIBackend is a backend for an Ethereum Registry.
type registryAPIBackend struct {
config *core.ChainConfig
config *params.ChainConfig
bc *core.BlockChain
chainDb ethdb.Database
txPool *core.TxPool
@@ -45,12 +46,12 @@ type registryAPIBackend struct {
// PrivateRegistarAPI offers various functions to access the Ethereum registry.
type PrivateRegistarAPI struct {
config *core.ChainConfig
config *params.ChainConfig
be *registryAPIBackend
}
// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance.
func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
func NewPrivateRegistarAPI(config *params.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
return &PrivateRegistarAPI{
config: config,
be: &registryAPIBackend{

View File

@@ -30,10 +30,10 @@ import (
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/internal/jsre"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params"
)
const (
@@ -100,10 +100,10 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
t.Fatalf("failed to create node: %v", err)
}
ethConf := &eth.Config{
ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)},
ChainConfig: &params.ChainConfig{ChainId: new(big.Int), HomesteadBlock: new(big.Int)},
Etherbase: common.HexToAddress(testAddress),
AccountManager: accman,
PowTest: true,
AccountManager: accman,
}
if confOverride != nil {
confOverride(ethConf)

View File

@@ -163,12 +163,12 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
// Generate a chain of b.N blocks using the supplied block
// generator function.
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{benchRootAddr, benchRootFunds})
chain, _ := GenerateChain(nil, genesis, db, b.N, gen)
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, b.N, gen)
// Time the insertion of the new chain.
// State and blocks are stored in the same DB.
evmux := new(event.TypeMux)
chainman, _ := NewBlockChain(db, &ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux)
chainman, _ := NewBlockChain(db, &params.ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux)
defer chainman.Stop()
b.ReportAllocs()
b.ResetTimer()

View File

@@ -41,13 +41,13 @@ var (
//
// BlockValidator implements Validator.
type BlockValidator struct {
config *ChainConfig // Chain configuration options
bc *BlockChain // Canonical block chain
Pow pow.PoW // Proof of work used for validating
config *params.ChainConfig // Chain configuration options
bc *BlockChain // Canonical block chain
Pow pow.PoW // Proof of work used for validating
}
// NewBlockValidator returns a new block validator which is safe for re-use
func NewBlockValidator(config *ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator {
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator {
validator := &BlockValidator{
config: config,
Pow: pow,
@@ -128,7 +128,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
}
// Validate the state root against the received state root and throw
// an error if they don't match.
if root := statedb.IntermediateRoot(); header.Root != root {
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
}
return nil
@@ -203,7 +203,7 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
// Validates a header. Returns an error if the header is invalid.
//
// See YP section 4.3.4. "Block Header Validity"
func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
}
@@ -248,13 +248,21 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare
}
}
// If all checks passed, validate the extra-data field for hard forks
return ValidateDAOHeaderExtraData(config, header)
if err := ValidateDAOHeaderExtraData(config, header); err != nil {
return err
}
if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 {
if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() {
return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash)
}
}
return nil
}
// 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 CalcDifficulty(config *ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff)
} else {

View File

@@ -27,11 +27,13 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow/ezp"
)
func testChainConfig() *ChainConfig {
return &ChainConfig{HomesteadBlock: big.NewInt(0)}
func testChainConfig() *params.ChainConfig {
return params.TestChainConfig
//return &params.ChainConfig{HomesteadBlock: big.NewInt(0)}
}
func proc() (Validator, *BlockChain) {
@@ -51,15 +53,15 @@ func TestNumber(t *testing.T) {
_, chain := proc()
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb)
header := makeHeader(chain.Genesis(), statedb)
header.Number = big.NewInt(3)
cfg := testChainConfig()
header := makeHeader(cfg, chain.Genesis(), statedb)
header.Number = big.NewInt(3)
err := ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
if err != BlockNumberErr {
t.Errorf("expected block number error, got %q", err)
}
header = makeHeader(chain.Genesis(), statedb)
header = makeHeader(cfg, chain.Genesis(), statedb)
err = ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
if err == BlockNumberErr {
t.Errorf("didn't expect block number error")

View File

@@ -38,6 +38,7 @@ import (
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
@@ -80,7 +81,7 @@ const (
// included in the canonical one where as GetBlockByNumber always represents the
// canonical chain.
type BlockChain struct {
config *ChainConfig // chain & network configuration
config *params.ChainConfig // chain & network configuration
hc *HeaderChain
chainDb ethdb.Database
@@ -95,10 +96,11 @@ type BlockChain struct {
currentBlock *types.Block // Current head of the block chain
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)
bodyCache *lru.Cache // Cache for the most recent block bodies
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
blockCache *lru.Cache // Cache for the most recent entire blocks
futureBlocks *lru.Cache // future blocks are blocks added for later processing
stateCache *state.StateDB // State database to reuse between imports (contains state cache)
bodyCache *lru.Cache // Cache for the most recent block bodies
bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format
blockCache *lru.Cache // Cache for the most recent entire blocks
futureBlocks *lru.Cache // future blocks are blocks added for later processing
quit chan struct{} // blockchain quit channel
running int32 // running must be called atomically
@@ -114,7 +116,7 @@ type BlockChain struct {
// NewBlockChain returns a fully initialised block chain using information
// available in the database. It initialiser the default Ethereum Validator and
// Processor.
func NewBlockChain(chainDb ethdb.Database, config *ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
bodyCache, _ := lru.New(bodyCacheLimit)
bodyRLPCache, _ := lru.New(bodyCacheLimit)
blockCache, _ := lru.New(blockCacheLimit)
@@ -198,11 +200,18 @@ func (self *BlockChain) loadLastState() error {
self.currentFastBlock = block
}
}
// Initialize a statedb cache to ensure singleton account bloom filter generation
statedb, err := state.New(self.currentBlock.Root(), self.chainDb)
if err != nil {
return err
}
self.stateCache = statedb
self.stateCache.GetAccount(common.Address{})
// Issue a status log and return
headerTd := self.GetTd(self.hc.CurrentHeader().Hash())
blockTd := self.GetTd(self.currentBlock.Hash())
fastTd := self.GetTd(self.currentFastBlock.Hash())
glog.V(logger.Info).Infof("Last header: #%d [%x…] TD=%v", self.hc.CurrentHeader().Number, self.hc.CurrentHeader().Hash().Bytes()[:4], headerTd)
glog.V(logger.Info).Infof("Last block: #%d [%x…] TD=%v", self.currentBlock.Number(), self.currentBlock.Hash().Bytes()[:4], blockTd)
glog.V(logger.Info).Infof("Fast block: #%d [%x…] TD=%v", self.currentFastBlock.Number(), self.currentFastBlock.Hash().Bytes()[:4], fastTd)
@@ -261,7 +270,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
if block == nil {
return fmt.Errorf("non existent block [%x…]", hash[:4])
}
if _, err := trie.NewSecure(block.Root(), self.chainDb); err != nil {
if _, err := trie.NewSecure(block.Root(), self.chainDb, 0); err != nil {
return err
}
// If all checks out, manually set the head block
@@ -349,7 +358,12 @@ func (self *BlockChain) AuxValidator() pow.PoW { return self.pow }
// State returns a new mutable state based on the current HEAD block.
func (self *BlockChain) State() (*state.StateDB, error) {
return state.New(self.CurrentBlock().Root(), self.chainDb)
return self.StateAt(self.CurrentBlock().Root())
}
// StateAt returns a new mutable state based on a particular point in time.
func (self *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
return self.stateCache.New(root)
}
// Reset purges the entire blockchain, restoring it to its genesis state.
@@ -770,6 +784,14 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err
localTd := self.GetTd(self.currentBlock.Hash())
externTd := new(big.Int).Add(block.Difficulty(), ptd)
// Irrelevant of the canonical status, write the block itself to the database
if err := self.hc.WriteTd(block.Hash(), externTd); err != nil {
glog.Fatalf("failed to write block total difficulty: %v", err)
}
if err := WriteBlock(self.chainDb, block); err != nil {
glog.Fatalf("failed to write block contents: %v", err)
}
// If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
@@ -780,20 +802,11 @@ func (self *BlockChain) WriteBlock(block *types.Block) (status WriteStatus, err
return NonStatTy, err
}
}
// Insert the block as the new head of the chain
self.insert(block)
self.insert(block) // Insert the block as the new head of the chain
status = CanonStatTy
} else {
status = SideStatTy
}
// Irrelevant of the canonical status, write the block itself to the database
if err := self.hc.WriteTd(block.Hash(), externTd); err != nil {
glog.Fatalf("failed to write block total difficulty: %v", err)
}
if err := WriteBlock(self.chainDb, block); err != nil {
glog.Fatalf("failed to write block contents: %v", err)
}
self.futureBlocks.Remove(block.Hash())
return
@@ -812,20 +825,16 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// faster than direct delivery and requires much less mutex
// acquiring.
var (
stats struct{ queued, processed, ignored int }
stats = insertStats{startTime: time.Now()}
events = make([]interface{}, 0, len(chain))
coalescedLogs vm.Logs
tstart = time.Now()
nonceChecked = make([]bool, len(chain))
statedb *state.StateDB
nonceChecked = make([]bool, len(chain))
)
// Start the parallel nonce verifier.
nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain)
defer close(nonceAbort)
txcount := 0
for i, block := range chain {
if atomic.LoadInt32(&self.procInterrupt) == 1 {
glog.V(logger.Debug).Infoln("Premature abort during block chain processing")
@@ -885,29 +894,30 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
// Create a new statedb using the parent block and report an
// error if it fails.
if statedb == nil {
statedb, err = state.New(self.GetBlock(block.ParentHash()).Root(), self.chainDb)
} else {
err = statedb.Reset(chain[i-1].Root())
switch {
case i == 0:
err = self.stateCache.Reset(self.GetBlock(block.ParentHash()).Root())
default:
err = self.stateCache.Reset(chain[i-1].Root())
}
if err != nil {
reportBlock(block, err)
return i, err
}
// Process block using the parent state as reference point.
receipts, logs, usedGas, err := self.processor.Process(block, statedb, self.config.VmConfig)
receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{})
if err != nil {
reportBlock(block, err)
return i, err
}
// Validate the state using the default validator
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash()), statedb, receipts, usedGas)
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash()), self.stateCache, receipts, usedGas)
if err != nil {
reportBlock(block, err)
return i, err
}
// Write state changes to database
_, err = statedb.Commit()
_, err = self.stateCache.Commit(self.config.IsEIP158(block.Number()))
if err != nil {
return i, err
}
@@ -919,7 +929,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
return i, err
}
txcount += len(block.Transactions())
// write the block to the chain and get the status
status, err := self.WriteBlock(block)
if err != nil {
@@ -954,19 +963,54 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
case SplitStatTy:
events = append(events, ChainSplitEvent{block, logs})
}
stats.processed++
if glog.V(logger.Info) {
stats.report(chain, i)
}
}
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
tend := time.Since(tstart)
start, end := chain[0], chain[len(chain)-1]
glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
}
go self.postChainEvents(events, coalescedLogs)
return 0, nil
}
// insertStats tracks and reports on block insertion.
type insertStats struct {
queued, processed, ignored int
lastIndex int
startTime time.Time
}
const (
statsReportLimit = 1024
statsReportTimeLimit = 8 * time.Second
)
// report prints statistics if some number of blocks have been processed
// or more than a few seconds have passed since the last message.
func (st *insertStats) report(chain []*types.Block, index int) {
limit := statsReportLimit
if index == len(chain)-1 {
limit = 0 // Always print a message for the last block.
}
now := time.Now()
duration := now.Sub(st.startTime)
if duration > statsReportTimeLimit || st.queued > limit || st.processed > limit || st.ignored > limit {
start, end := chain[st.lastIndex], chain[index]
txcount := countTransactions(chain[st.lastIndex : index+1])
glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", st.processed, st.queued, st.ignored, txcount, duration, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
*st = insertStats{startTime: now, lastIndex: index}
}
}
func countTransactions(chain []*types.Block) (c int) {
for _, b := range chain {
c += len(b.Transactions())
}
return c
}
// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
// to be part of the new canonical chain and accumulates potential missing transactions and post an
// event about them
@@ -1216,4 +1260,4 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
}
// Config retrieves the blockchain's chain configuration.
func (self *BlockChain) Config() *ChainConfig { return self.config }
func (self *BlockChain) Config() *params.ChainConfig { return self.config }

View File

@@ -154,7 +154,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
blockchain.mu.Lock()
WriteTd(blockchain.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash())))
WriteBlock(blockchain.chainDb, block)
statedb.Commit()
statedb.Commit(false)
blockchain.mu.Unlock()
}
return nil
@@ -712,7 +712,7 @@ func TestFastVsFullChains(t *testing.T) {
funds = big.NewInt(1000000000)
genesis = GenesisBlockForTesting(gendb, address, funds)
)
blocks, receipts := GenerateChain(nil, genesis, gendb, 1024, func(i int, block *BlockGen) {
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, 1024, func(i int, block *BlockGen) {
block.SetCoinbase(common.Address{0x00})
// If the block number is multiple of 3, send a few bonus transactions to the miner
@@ -795,7 +795,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
genesis = GenesisBlockForTesting(gendb, address, funds)
)
height := uint64(1024)
blocks, receipts := GenerateChain(nil, genesis, gendb, int(height), nil)
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, int(height), nil)
// Configure a subchain to roll back
remove := []common.Hash{}
@@ -895,7 +895,7 @@ func TestChainTxReorgs(t *testing.T) {
// - futureAdd: transaction added after the reorg has already finished
var pastAdd, freshAdd, futureAdd *types.Transaction
chain, _ := GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {
switch i {
case 0:
pastDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2)
@@ -920,7 +920,7 @@ func TestChainTxReorgs(t *testing.T) {
}
// overwrite the old chain
chain, _ = GenerateChain(nil, genesis, db, 5, func(i int, gen *BlockGen) {
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
switch i {
case 0:
pastAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3)
@@ -990,7 +990,7 @@ func TestLogReorgs(t *testing.T) {
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
subs := evmux.Subscribe(RemovedLogsEvent{})
chain, _ := GenerateChain(nil, genesis, db, 2, func(i int, gen *BlockGen) {
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) {
if i == 1 {
tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code).SignECDSA(key1)
if err != nil {
@@ -1003,7 +1003,7 @@ func TestLogReorgs(t *testing.T) {
t.Fatalf("failed to insert chain: %v", err)
}
chain, _ = GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {})
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {})
if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert forked chain: %v", err)
}
@@ -1025,12 +1025,12 @@ func TestReorgSideEvent(t *testing.T) {
evmux := &event.TypeMux{}
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
chain, _ := GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {})
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {})
if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert chain: %v", err)
}
replacementBlocks, _ := GenerateChain(nil, genesis, db, 4, func(i int, gen *BlockGen) {
replacementBlocks, _ := GenerateChain(params.TestChainConfig, genesis, db, 4, func(i int, gen *BlockGen) {
tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key1)
if i == 2 {
gen.OffsetTime(-1)
@@ -1090,3 +1090,144 @@ done:
}
}
// Tests if the canonical block can be fetched from the database during chain insertion.
func TestCanonicalBlockRetrieval(t *testing.T) {
var (
db, _ = ethdb.NewMemDatabase()
genesis = WriteGenesisBlockForTesting(db)
)
evmux := &event.TypeMux{}
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *BlockGen) {})
for i, _ := range chain {
go func(block *types.Block) {
// try to retrieve a block by its canonical hash and see if the block data can be retrieved.
for {
ch := GetCanonicalHash(db, block.NumberU64())
if ch == (common.Hash{}) {
continue // busy wait for canonical hash to be written
}
if ch != block.Hash() {
t.Fatalf("unknown canonical hash, want %s, got %s", block.Hash().Hex(), ch.Hex())
}
fb := GetBlock(db, ch)
if fb == nil {
t.Fatalf("unable to retrieve block %d for canonical hash: %s", block.NumberU64(), ch.Hex())
}
if fb.Hash() != block.Hash() {
t.Fatalf("invalid block hash for block %d, want %s, got %s", block.NumberU64(), block.Hash().Hex(), fb.Hash().Hex())
}
return
}
}(chain[i])
blockchain.InsertChain(types.Blocks{chain[i]})
}
}
func TestEIP155Transition(t *testing.T) {
// Configure and generate a sample block chain
var (
db, _ = ethdb.NewMemDatabase()
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
funds = big.NewInt(1000000000)
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds})
config = &params.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
mux event.TypeMux
)
blockchain, _ := NewBlockChain(db, config, FakePow{}, &mux)
blocks, _ := GenerateChain(config, genesis, db, 4, func(i int, block *BlockGen) {
var (
tx *types.Transaction
err error
basicTx = func(signer types.Signer) (*types.Transaction, error) {
tx := types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil)
tx.SetSigner(signer)
return tx.SignECDSA(key)
}
)
switch i {
case 0:
tx, err = basicTx(types.HomesteadSigner{})
if err != nil {
t.Fatal(err)
}
block.AddTx(tx)
case 2:
tx, err = basicTx(types.HomesteadSigner{})
if err != nil {
t.Fatal(err)
}
block.AddTx(tx)
tx, err = basicTx(types.NewEIP155Signer(config.ChainId))
if err != nil {
t.Fatal(err)
}
block.AddTx(tx)
case 3:
tx, err = basicTx(types.HomesteadSigner{})
if err != nil {
t.Fatal(err)
}
block.AddTx(tx)
tx, err = basicTx(types.NewEIP155Signer(config.ChainId))
if err != nil {
t.Fatal(err)
}
block.AddTx(tx)
}
})
if _, err := blockchain.InsertChain(blocks); err != nil {
t.Fatal(err)
}
block := blockchain.GetBlockByNumber(1)
if block.Transactions()[0].Protected() {
t.Error("Expected block[0].txs[0] to not be replay protected")
}
block = blockchain.GetBlockByNumber(3)
if block.Transactions()[0].Protected() {
t.Error("Expected block[3].txs[0] to not be replay protected")
}
if !block.Transactions()[1].Protected() {
t.Error("Expected block[3].txs[1] to be replay protected")
}
if _, err := blockchain.InsertChain(blocks[4:]); err != nil {
t.Fatal(err)
}
// generate an invalid chain id transaction
config = &params.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) {
var (
tx *types.Transaction
err error
basicTx = func(signer types.Signer) (*types.Transaction, error) {
tx := types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil)
tx.SetSigner(signer)
return tx.SignECDSA(key)
}
)
switch i {
case 0:
tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2)))
if err != nil {
t.Fatal(err)
}
block.AddTx(tx)
}
})
_, err := blockchain.InsertChain(blocks)
if err != types.ErrInvalidChainId {
t.Error("expected error:", types.ErrInvalidChainId)
}
}

View File

@@ -35,8 +35,8 @@ import (
*/
// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings.
func MakeChainConfig() *ChainConfig {
return &ChainConfig{
func MakeChainConfig() *params.ChainConfig {
return &params.ChainConfig{
HomesteadBlock: big.NewInt(0),
DAOForkBlock: nil,
DAOForkSupport: true,
@@ -73,6 +73,8 @@ type BlockGen struct {
txs []*types.Transaction
receipts []*types.Receipt
uncles []*types.Header
config *params.ChainConfig
}
// SetCoinbase sets the coinbase of the generated block.
@@ -106,7 +108,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
b.SetCoinbase(common.Address{})
}
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs))
receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
if err != nil {
panic(err)
}
@@ -131,7 +133,7 @@ func (b *BlockGen) AddUncheckedReceipt(receipt *types.Receipt) {
// TxNonce returns the next valid transaction nonce for the
// account at addr. It panics if the account does not exist.
func (b *BlockGen) TxNonce(addr common.Address) uint64 {
if !b.statedb.HasAccount(addr) {
if !b.statedb.Exist(addr) {
panic("account does not exist")
}
return b.statedb.GetNonce(addr)
@@ -178,10 +180,10 @@ func (b *BlockGen) OffsetTime(seconds int64) {
// Blocks created by GenerateChain do not contain valid proof of work
// values. Inserting them into BlockChain requires use of FakePow or
// a similar non-validating proof of work implementation.
func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) {
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb}
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config}
// Mutate the state and block according to any hard-fork specs
if config == nil {
@@ -203,7 +205,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
gen(i, b)
}
AccumulateRewards(statedb, h, b.uncles)
root, err := statedb.Commit()
root, err := statedb.Commit(config.IsEIP158(h.Number))
if err != nil {
panic(fmt.Sprintf("state write error: %v", err))
}
@@ -215,7 +217,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
if err != nil {
panic(err)
}
header := makeHeader(parent, statedb)
header := makeHeader(config, parent, statedb)
block, receipt := genblock(i, header, statedb)
blocks[i] = block
receipts[i] = receipt
@@ -224,7 +226,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
return blocks, receipts
}
func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.StateDB) *types.Header {
var time *big.Int
if parent.Time() == nil {
time = big.NewInt(10)
@@ -232,7 +234,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds
}
return &types.Header{
Root: state.IntermediateRoot(),
Root: state.IntermediateRoot(config.IsEIP158(parent.Number())),
ParentHash: parent.Hash(),
Coinbase: parent.Coinbase(),
Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
@@ -283,7 +285,7 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [
// makeBlockChain creates a deterministic chain of blocks rooted at parent.
func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block {
blocks, _ := GenerateChain(nil, parent, db, n, func(i int, b *BlockGen) {
blocks, _ := GenerateChain(params.TestChainConfig, parent, db, n, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
})
return blocks

View File

@@ -41,13 +41,16 @@ func ExampleGenerateChain() {
db, _ = ethdb.NewMemDatabase()
)
chainConfig := &params.ChainConfig{
HomesteadBlock: new(big.Int),
}
// Ensure that key1 has some funds in the genesis block.
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(1000000)})
// This call generates a chain of 5 blocks. The function runs for
// each block and adds different features to gen based on the
// block index.
chain, _ := GenerateChain(nil, genesis, db, 5, func(i int, gen *BlockGen) {
chain, _ := GenerateChain(chainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
switch i {
case 0:
// In block 1, addr1 sends addr2 some ether.
@@ -77,9 +80,9 @@ func ExampleGenerateChain() {
// Import the chain. This runs all block validation rules.
evmux := &event.TypeMux{}
blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux)
blockchain, _ := NewBlockChain(db, chainConfig, FakePow{}, evmux)
if i, err := blockchain.InsertChain(chain); err != nil {
fmt.Printf("insert error (block %d): %v\n", i, err)
fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
return
}

View File

@@ -25,6 +25,7 @@ import (
"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/pow"
)
@@ -60,7 +61,7 @@ func TestPowVerification(t *testing.T) {
var (
testdb, _ = ethdb.NewMemDatabase()
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
blocks, _ = GenerateChain(nil, genesis, testdb, 8, nil)
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
)
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -115,7 +116,7 @@ func testPowConcurrentVerification(t *testing.T, threads int) {
var (
testdb, _ = ethdb.NewMemDatabase()
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
blocks, _ = GenerateChain(nil, genesis, testdb, 8, nil)
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
)
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {
@@ -186,7 +187,7 @@ func testPowConcurrentAbortion(t *testing.T, threads int) {
var (
testdb, _ = ethdb.NewMemDatabase()
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
blocks, _ = GenerateChain(nil, genesis, testdb, 1024, nil)
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil)
)
headers := make([]*types.Header, len(blocks))
for i, block := range blocks {

View File

@@ -1,47 +0,0 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"errors"
"math/big"
"github.com/ethereum/go-ethereum/core/vm"
)
var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
// ChainConfig is the core config which determines the blockchain settings.
//
// ChainConfig is stored in the database on a per block basis. This means
// that any network, identified by its genesis block, can have its own
// set of configuration options.
type ChainConfig struct {
HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead)
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork)
DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork
VmConfig vm.Config `json:"-"`
}
// IsHomestead returns whether num is either equal to the homestead block or greater.
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
if c.HomesteadBlock == nil || num == nil {
return false
}
return num.Cmp(c.HomesteadBlock) >= 0
}

View File

@@ -33,7 +33,7 @@ import (
// with the fork specific extra-data set
// b) if the node is pro-fork, require blocks in the specific range to have the
// unique extra-data set.
func ValidateDAOHeaderExtraData(config *ChainConfig, header *types.Header) error {
func ValidateDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error {
// Short circuit validation if the node doesn't care about the DAO fork
if config.DAOForkBlock == nil {
return nil

View File

@@ -33,17 +33,17 @@ func TestDAOForkRangeExtradata(t *testing.T) {
// Generate a common prefix for both pro-forkers and non-forkers
db, _ := ethdb.NewMemDatabase()
genesis := WriteGenesisBlockForTesting(db)
prefix, _ := GenerateChain(nil, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
// Create the concurrent, conflicting two nodes
proDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(proDb)
proConf := &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true}
proConf := &params.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true}
proBc, _ := NewBlockChain(proDb, proConf, new(FakePow), new(event.TypeMux))
conDb, _ := ethdb.NewMemDatabase()
WriteGenesisBlockForTesting(conDb)
conConf := &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false}
conConf := &params.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false}
conBc, _ := NewBlockChain(conDb, conConf, new(FakePow), new(event.TypeMux))
if _, err := proBc.InsertChain(prefix); err != nil {

View File

@@ -20,6 +20,7 @@ import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"math/big"
@@ -28,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -52,7 +54,8 @@ var (
blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually]
configPrefix = []byte("ethereum-config-") // config prefix for the db
configPrefix = []byte("ethereum-config-") // config prefix for the db
ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
)
// GetCanonicalHash retrieves a hash assigned to a canonical block number.
@@ -157,7 +160,11 @@ func GetTd(db ethdb.Database, hash common.Hash) *big.Int {
}
// GetBlock retrieves an entire block corresponding to the hash, assembling it
// back from the stored header and body.
// back from the stored header and body. If either the header or body could not
// be retrieved nil is returned.
//
// Note, due to concurrent download of header and block body the header and thus
// canonical hash can be stored in the database but the body data not (yet).
func GetBlock(db ethdb.Database, hash common.Hash) *types.Block {
// Retrieve the block header and body contents
header := GetHeader(db, hash)
@@ -532,7 +539,7 @@ func WriteBlockChainVersion(db ethdb.Database, vsn int) {
}
// WriteChainConfig writes the chain config settings to the database.
func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) error {
func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *params.ChainConfig) error {
// short circuit and ignore if nil config. GetChainConfig
// will return a default.
if cfg == nil {
@@ -548,13 +555,13 @@ func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) err
}
// GetChainConfig will fetch the network settings based on the given hash.
func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) {
func GetChainConfig(db ethdb.Database, hash common.Hash) (*params.ChainConfig, error) {
jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...))
if len(jsonChainConfig) == 0 {
return nil, ChainConfigNotFoundErr
}
var config ChainConfig
var config params.ChainConfig
if err := json.Unmarshal(jsonChainConfig, &config); err != nil {
return nil, err
}

View File

@@ -30,6 +30,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
@@ -62,7 +63,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
return nil
}
func TestDifficultyFrontier(t *testing.T) {
func TestCalcDifficulty(t *testing.T) {
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
if err != nil {
t.Fatal(err)
@@ -75,9 +76,10 @@ func TestDifficultyFrontier(t *testing.T) {
t.Fatal(err)
}
config := &params.ChainConfig{HomesteadBlock: big.NewInt(1150000)}
for name, test := range tests {
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
diff := calcDifficultyFrontier(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
if diff.Cmp(test.CurrentDifficulty) != 0 {
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
}
@@ -561,7 +563,7 @@ func TestMipmapChain(t *testing.T) {
defer db.Close()
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr, big.NewInt(1000000)})
chain, receipts := GenerateChain(nil, genesis, db, 1010, func(i int, gen *BlockGen) {
chain, receipts := GenerateChain(params.TestChainConfig, genesis, db, 1010, func(i int, gen *BlockGen) {
var receipts types.Receipts
switch i {
case 1:

View File

@@ -27,14 +27,14 @@ import (
// Call executes within the given contract
func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
ret, _, err = exec(true, env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value)
return ret, err
}
// CallCode executes the given address' code as the given contract address
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
callerAddr := caller.Address()
ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
ret, _, err = exec(false, env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value)
return ret, err
}
@@ -43,13 +43,13 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address
callerAddr := caller.Address()
originAddr := env.Origin()
callerValue := caller.Value()
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
return ret, err
}
// Create creates a new contract with the given code
func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
ret, address, err = exec(env, caller, nil, nil, nil, code, gas, gasPrice, value)
ret, address, err = exec(true, env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, gasPrice, value)
// Here we get an error if we run into maximum stack depth,
// See: https://github.com/ethereum/yellowpaper/pull/131
// and YP definitions for CREATE instruction
@@ -59,7 +59,7 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric
return ret, address, err
}
func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
evm := env.Vm()
// Depth check execution. Fail if we're trying to execute above the
// limit.
@@ -85,27 +85,37 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
createAccount = true
}
snapshotPreTransfer := env.MakeSnapshot()
snapshotPreTransfer := env.SnapshotDatabase()
var (
from = env.Db().GetAccount(caller.Address())
to vm.Account
)
if createAccount {
to = env.Db().CreateAccount(*address)
if env.ChainConfig().IsEIP158(env.BlockNumber()) {
env.Db().SetNonce(*address, 1)
}
} else {
if !env.Db().Exist(*address) {
if vm.Precompiled[address.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 {
caller.ReturnGas(gas, gasPrice)
return nil, common.Address{}, nil
}
to = env.Db().CreateAccount(*address)
} else {
to = env.Db().GetAccount(*address)
}
}
env.Transfer(from, to, value)
if transfers {
env.Transfer(from, to, 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 := vm.NewContract(caller, to, value, gas, gasPrice)
contract.SetCallCode(codeAddr, code)
contract.SetCallCode(codeAddr, codeHash, code)
defer contract.Finalise()
ret, err = evm.Run(contract, input)
@@ -126,16 +136,16 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
// 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 && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
if err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
contract.UseGas(contract.Gas)
env.SetSnapshot(snapshotPreTransfer)
env.RevertToSnapshot(snapshotPreTransfer)
}
return ret, addr, err
}
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
evm := env.Vm()
// Depth check execution. Fail if we're trying to execute above the
// limit.
@@ -144,7 +154,7 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
return nil, common.Address{}, vm.DepthError
}
snapshot := env.MakeSnapshot()
snapshot := env.SnapshotDatabase()
var to vm.Account
if !env.Db().Exist(*toAddr) {
@@ -155,14 +165,14 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
// Iinitialise a new contract and make initialise the delegate values
contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
contract.SetCallCode(codeAddr, code)
contract.SetCallCode(codeAddr, codeHash, code)
defer contract.Finalise()
ret, err = evm.Run(contract, input)
if err != nil {
contract.UseGas(contract.Gas)
env.SetSnapshot(snapshot)
env.RevertToSnapshot(snapshot)
}
return ret, addr, err

View File

@@ -43,7 +43,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
}
var genesis struct {
ChainConfig *ChainConfig `json:"config"`
ChainConfig *params.ChainConfig `json:"config"`
Nonce string
Timestamp string
ParentHash string
@@ -73,7 +73,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
statedb.SetState(address, common.HexToHash(key), common.HexToHash(value))
}
}
root, stateBatch := statedb.CommitBatch()
root, stateBatch := statedb.CommitBatch(false)
difficulty := common.String2Big(genesis.Difficulty)
block := types.NewBlock(&types.Header{
@@ -128,7 +128,7 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big
statedb, _ := state.New(common.Hash{}, db)
obj := statedb.GetOrNewStateObject(addr)
obj.SetBalance(balance)
root, err := statedb.Commit()
root, err := statedb.Commit(false)
if err != nil {
panic(fmt.Sprintf("cannot write state: %v", err))
}

View File

@@ -31,6 +31,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/pow"
"github.com/hashicorp/golang-lru"
)
@@ -41,7 +42,7 @@ import (
// It is not thread safe either, the encapsulating chain structures should do
// the necessary mutex locking/unlocking.
type HeaderChain struct {
config *ChainConfig
config *params.ChainConfig
chainDb ethdb.Database
genesisHeader *types.Header
@@ -65,7 +66,7 @@ type getHeaderValidatorFn func() HeaderValidator
// getValidator should return the parent's validator
// procInterrupt points to the parent's interrupt semaphore
// wg points to the parent's shutdown wait group
func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
headerCache, _ := lru.New(headerCacheLimit)
tdCache, _ := lru.New(tdCacheLimit)
@@ -129,6 +130,14 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
localTd := hc.GetTd(hc.currentHeaderHash)
externTd := new(big.Int).Add(header.Difficulty, ptd)
// Irrelevant of the canonical status, write the td and header to the database
if err := hc.WriteTd(hash, externTd); err != nil {
glog.Fatalf("failed to write header total difficulty: %v", err)
}
if err := WriteHeader(hc.chainDb, header); err != nil {
glog.Fatalf("failed to write header contents: %v", err)
}
// If the total difficulty is higher than our known, add it to the canonical chain
// Second clause in the if statement reduces the vulnerability to selfish mining.
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
@@ -150,6 +159,7 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
headHeader = hc.GetHeader(headHash)
headNumber = headHeader.Number.Uint64()
}
// Extend the canonical chain with the new header
if err := WriteCanonicalHash(hc.chainDb, hash, number); err != nil {
glog.Fatalf("failed to insert header number: %v", err)
@@ -157,19 +167,13 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
glog.Fatalf("failed to insert head header hash: %v", err)
}
hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header)
status = CanonStatTy
} else {
status = SideStatTy
}
// Irrelevant of the canonical status, write the header itself to the database
if err := hc.WriteTd(hash, externTd); err != nil {
glog.Fatalf("failed to write header total difficulty: %v", err)
}
if err := WriteHeader(hc.chainDb, header); err != nil {
glog.Fatalf("failed to write header contents: %v", err)
}
hc.headerCache.Add(hash, header)
return
@@ -440,13 +444,13 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) {
//
// headerValidator implements HeaderValidator.
type headerValidator struct {
config *ChainConfig
config *params.ChainConfig
hc *HeaderChain // Canonical header chain
Pow pow.PoW // Proof of work used for validating
}
// NewBlockValidator returns a new block validator which is safe for re-use
func NewHeaderValidator(config *ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator {
func NewHeaderValidator(config *params.ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator {
return &headerValidator{
config: config,
Pow: pow,

View File

@@ -21,9 +21,10 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)
type Account struct {
type DumpAccount struct {
Balance string `json:"balance"`
Nonce uint64 `json:"nonce"`
Root string `json:"root"`
@@ -32,40 +33,41 @@ type Account struct {
Storage map[string]string `json:"storage"`
}
type World struct {
Root string `json:"root"`
Accounts map[string]Account `json:"accounts"`
type Dump struct {
Root string `json:"root"`
Accounts map[string]DumpAccount `json:"accounts"`
}
func (self *StateDB) RawDump() World {
world := World{
func (self *StateDB) RawDump() Dump {
dump := Dump{
Root: common.Bytes2Hex(self.trie.Root()),
Accounts: make(map[string]Account),
Accounts: make(map[string]DumpAccount),
}
it := self.trie.Iterator()
for it.Next() {
addr := self.trie.GetKey(it.Key)
stateObject, err := DecodeObject(common.BytesToAddress(addr), self.db, it.Value)
if err != nil {
var data Account
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
panic(err)
}
account := Account{
Balance: stateObject.balance.String(),
Nonce: stateObject.nonce,
Root: common.Bytes2Hex(stateObject.Root()),
CodeHash: common.Bytes2Hex(stateObject.codeHash),
Code: common.Bytes2Hex(stateObject.Code()),
obj := newObject(nil, common.BytesToAddress(addr), data, nil)
account := DumpAccount{
Balance: data.Balance.String(),
Nonce: data.Nonce,
Root: common.Bytes2Hex(data.Root[:]),
CodeHash: common.Bytes2Hex(data.CodeHash),
Code: common.Bytes2Hex(obj.Code(self.db)),
Storage: make(map[string]string),
}
storageIt := stateObject.trie.Iterator()
storageIt := obj.getTrie(self.db).Iterator()
for storageIt.Next() {
account.Storage[common.Bytes2Hex(self.trie.GetKey(storageIt.Key))] = common.Bytes2Hex(storageIt.Value)
}
world.Accounts[common.Bytes2Hex(addr)] = account
dump.Accounts[common.Bytes2Hex(addr)] = account
}
return world
return dump
}
func (self *StateDB) Dump() []byte {
@@ -76,12 +78,3 @@ func (self *StateDB) Dump() []byte {
return json
}
// Debug stuff
func (self *StateObject) CreateOutputForDiff() {
fmt.Printf("%x %x %x %x\n", self.Address(), self.Root(), self.balance.Bytes(), self.nonce)
it := self.trie.Iterator()
for it.Next() {
fmt.Printf("%x %x\n", it.Key, it.Value)
}
}

View File

@@ -76,7 +76,7 @@ func (it *NodeIterator) step() error {
}
// Initialize the iterator if we've just started
if it.stateIt == nil {
it.stateIt = trie.NewNodeIterator(it.state.trie.Trie)
it.stateIt = it.state.trie.NodeIterator()
}
// If we had data nodes previously, we surely have at least state nodes
if it.dataIt != nil {

117
core/state/journal.go Normal file
View File

@@ -0,0 +1,117 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package state
import (
"math/big"
"github.com/ethereum/go-ethereum/common"
)
type journalEntry interface {
undo(*StateDB)
}
type journal []journalEntry
type (
// Changes to the account trie.
createObjectChange struct {
account *common.Address
}
resetObjectChange struct {
prev *StateObject
}
suicideChange struct {
account *common.Address
prev bool // whether account had already suicided
prevbalance *big.Int
}
// Changes to individual accounts.
balanceChange struct {
account *common.Address
prev *big.Int
}
nonceChange struct {
account *common.Address
prev uint64
}
storageChange struct {
account *common.Address
key, prevalue common.Hash
}
codeChange struct {
account *common.Address
prevcode, prevhash []byte
}
// Changes to other state values.
refundChange struct {
prev *big.Int
}
addLogChange struct {
txhash common.Hash
}
)
func (ch createObjectChange) undo(s *StateDB) {
s.GetStateObject(*ch.account).deleted = true
delete(s.stateObjects, *ch.account)
delete(s.stateObjectsDirty, *ch.account)
}
func (ch resetObjectChange) undo(s *StateDB) {
s.setStateObject(ch.prev)
}
func (ch suicideChange) undo(s *StateDB) {
obj := s.GetStateObject(*ch.account)
if obj != nil {
obj.suicided = ch.prev
obj.setBalance(ch.prevbalance)
}
}
func (ch balanceChange) undo(s *StateDB) {
s.GetStateObject(*ch.account).setBalance(ch.prev)
}
func (ch nonceChange) undo(s *StateDB) {
s.GetStateObject(*ch.account).setNonce(ch.prev)
}
func (ch codeChange) undo(s *StateDB) {
s.GetStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
}
func (ch storageChange) undo(s *StateDB) {
s.GetStateObject(*ch.account).setState(ch.key, ch.prevalue)
}
func (ch refundChange) undo(s *StateDB) {
s.refund = ch.prev
}
func (ch addLogChange) undo(s *StateDB) {
logs := s.logs[ch.txhash]
if len(logs) == 1 {
delete(s.logs, ch.txhash)
} else {
s.logs[ch.txhash] = logs[:len(logs)-1]
}
}

View File

@@ -33,14 +33,14 @@ type ManagedState struct {
mu sync.RWMutex
accounts map[string]*account
accounts map[common.Address]*account
}
// ManagedState returns a new managed state with the statedb as it's backing layer
func ManageState(statedb *StateDB) *ManagedState {
return &ManagedState{
StateDB: statedb.Copy(),
accounts: make(map[string]*account),
accounts: make(map[common.Address]*account),
}
}
@@ -103,7 +103,7 @@ func (ms *ManagedState) SetNonce(addr common.Address, nonce uint64) {
so := ms.GetOrNewStateObject(addr)
so.SetNonce(nonce)
ms.accounts[addr.Str()] = newAccount(so)
ms.accounts[addr] = newAccount(so)
}
// HasAccount returns whether the given address is managed or not
@@ -114,29 +114,28 @@ func (ms *ManagedState) HasAccount(addr common.Address) bool {
}
func (ms *ManagedState) hasAccount(addr common.Address) bool {
_, ok := ms.accounts[addr.Str()]
_, ok := ms.accounts[addr]
return ok
}
// populate the managed state
func (ms *ManagedState) getAccount(addr common.Address) *account {
straddr := addr.Str()
if account, ok := ms.accounts[straddr]; !ok {
if account, ok := ms.accounts[addr]; !ok {
so := ms.GetOrNewStateObject(addr)
ms.accounts[straddr] = newAccount(so)
ms.accounts[addr] = newAccount(so)
} else {
// Always make sure the state account nonce isn't actually higher
// than the tracked one.
so := ms.StateDB.GetStateObject(addr)
if so != nil && uint64(len(account.nonces))+account.nstart < so.nonce {
ms.accounts[straddr] = newAccount(so)
if so != nil && uint64(len(account.nonces))+account.nstart < so.Nonce() {
ms.accounts[addr] = newAccount(so)
}
}
return ms.accounts[straddr]
return ms.accounts[addr]
}
func newAccount(so *StateObject) *account {
return &account{so, so.nonce, nil}
return &account{so, so.Nonce(), nil}
}

View File

@@ -29,11 +29,9 @@ func create() (*ManagedState, *account) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := New(common.Hash{}, db)
ms := ManageState(statedb)
so := &StateObject{address: addr, nonce: 100}
ms.StateDB.stateObjects[addr.Str()] = so
ms.accounts[addr.Str()] = newAccount(so)
return ms, ms.accounts[addr.Str()]
ms.StateDB.SetNonce(addr, 100)
ms.accounts[addr] = newAccount(ms.StateDB.GetStateObject(addr))
return ms, ms.accounts[addr]
}
func TestNewNonce(t *testing.T) {
@@ -92,7 +90,7 @@ func TestRemoteNonceChange(t *testing.T) {
account.nonces = append(account.nonces, nn...)
nonce := ms.NewNonce(addr)
ms.StateDB.stateObjects[addr.Str()].nonce = 200
ms.StateDB.stateObjects[addr].data.Nonce = 200
nonce = ms.NewNonce(addr)
if nonce != 200 {
t.Error("expected nonce after remote update to be", 201, "got", nonce)
@@ -100,7 +98,7 @@ func TestRemoteNonceChange(t *testing.T) {
ms.NewNonce(addr)
ms.NewNonce(addr)
ms.NewNonce(addr)
ms.StateDB.stateObjects[addr.Str()].nonce = 200
ms.StateDB.stateObjects[addr].data.Nonce = 200
nonce = ms.NewNonce(addr)
if nonce != 204 {
t.Error("expected nonce after remote update to be", 201, "got", nonce)

View File

@@ -57,143 +57,231 @@ func (self Storage) Copy() Storage {
return cpy
}
// StateObject represents an Ethereum account which is being modified.
//
// The usage pattern is as follows:
// First you need to obtain a state object.
// Account values can be accessed and modified through the object.
// Finally, call CommitTrie to write the modified storage trie into a database.
type StateObject struct {
db trie.Database // State database for storing state changes
trie *trie.SecureTrie
address common.Address // Ethereum address of this account
data Account
db *StateDB
// Address belonging to this account
address common.Address
// The balance of the account
balance *big.Int
// The nonce of the account
nonce uint64
// The code hash if code is present (i.e. a contract)
codeHash []byte
// The code for this account
code Code
// Temporarily initialisation code
initCode Code
// Cached storage (flushed when updated)
storage Storage
// DB error.
// State objects are used by the consensus core and VM which are
// unable to deal with database-level errors. Any error that occurs
// during a database read is memoized here and will eventually be returned
// by StateDB.Commit.
dbErr error
// Mark for deletion
// When an object is marked for deletion it will be delete from the trie
// during the "update" phase of the state transition
remove bool
deleted bool
dirty bool
// Write caches.
trie *trie.SecureTrie // storage trie, which becomes non-nil on first access
code Code // contract bytecode, which gets set when code is loaded
cachedStorage Storage // Storage entry cache to avoid duplicate reads
dirtyStorage Storage // Storage entries that need to be flushed to disk
// Cache flags.
// When an object is marked suicided it will be delete from the trie
// during the "update" phase of the state transition.
dirtyCode bool // true if the code was updated
suicided bool
deleted bool
onDirty func(addr common.Address) // Callback method to mark a state object newly dirty
}
func NewStateObject(address common.Address, db trie.Database) *StateObject {
object := &StateObject{
db: db,
address: address,
balance: new(big.Int),
dirty: true,
codeHash: emptyCodeHash,
storage: make(Storage),
// empty returns whether the account is considered empty.
func (s *StateObject) empty() bool {
return s.data.Nonce == 0 && s.data.Balance.BitLen() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
}
// Account is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
type Account struct {
Nonce uint64
Balance *big.Int
Root common.Hash // merkle root of the storage trie
CodeHash []byte
}
// newObject creates a state object.
func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *StateObject {
if data.Balance == nil {
data.Balance = new(big.Int)
}
object.trie, _ = trie.NewSecure(common.Hash{}, db)
return object
if data.CodeHash == nil {
data.CodeHash = emptyCodeHash
}
return &StateObject{db: db, address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty}
}
func (self *StateObject) MarkForDeletion() {
self.remove = true
self.dirty = true
// EncodeRLP implements rlp.Encoder.
func (c *StateObject) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, c.data)
}
// setError remembers the first non-nil error it is called with.
func (self *StateObject) setError(err error) {
if self.dbErr == nil {
self.dbErr = err
}
}
func (self *StateObject) markSuicided() {
self.suicided = true
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
if glog.V(logger.Core) {
glog.Infof("%x: #%d %v X\n", self.Address(), self.nonce, self.balance)
glog.Infof("%x: #%d %v X\n", self.Address(), self.Nonce(), self.Balance())
}
}
func (c *StateObject) getAddr(addr common.Hash) common.Hash {
var ret []byte
rlp.DecodeBytes(c.trie.Get(addr[:]), &ret)
return common.BytesToHash(ret)
}
func (c *StateObject) setAddr(addr, value common.Hash) {
v, err := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
if err != nil {
// if RLPing failed we better panic and not fail silently. This would be considered a consensus issue
panic(err)
}
c.trie.Update(addr[:], v)
}
func (self *StateObject) Storage() Storage {
return self.storage
}
func (self *StateObject) GetState(key common.Hash) common.Hash {
value, exists := self.storage[key]
if !exists {
value = self.getAddr(key)
if (value != common.Hash{}) {
self.storage[key] = value
func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
if c.trie == nil {
var err error
c.trie, err = trie.NewSecure(c.data.Root, db, 0)
if err != nil {
c.trie, _ = trie.NewSecure(common.Hash{}, db, 0)
c.setError(fmt.Errorf("can't create storage trie: %v", err))
}
}
return c.trie
}
// GetState returns a value in account storage.
func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash {
value, exists := self.cachedStorage[key]
if exists {
return value
}
// Load from DB in case it is missing.
if enc := self.getTrie(db).Get(key[:]); len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
self.setError(err)
}
value.SetBytes(content)
}
if (value != common.Hash{}) {
self.cachedStorage[key] = value
}
return value
}
func (self *StateObject) SetState(key, value common.Hash) {
self.storage[key] = value
self.dirty = true
// SetState updates a value in account storage.
func (self *StateObject) SetState(db trie.Database, key, value common.Hash) {
self.db.journal = append(self.db.journal, storageChange{
account: &self.address,
key: key,
prevalue: self.GetState(db, key),
})
self.setState(key, value)
}
// Update updates the current cached storage to the trie
func (self *StateObject) Update() {
for key, value := range self.storage {
func (self *StateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value
self.dirtyStorage[key] = value
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
// updateTrie writes cached storage modifications into the object's storage trie.
func (self *StateObject) updateTrie(db trie.Database) {
tr := self.getTrie(db)
for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key)
if (value == common.Hash{}) {
self.trie.Delete(key[:])
tr.Delete(key[:])
continue
}
self.setAddr(key, value)
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
tr.Update(key[:], v)
}
}
// UpdateRoot sets the trie root to the current root hash of
func (self *StateObject) updateRoot(db trie.Database) {
self.updateTrie(db)
self.data.Root = self.trie.Hash()
}
// CommitTrie the storage trie of the object to dwb.
// This updates the trie root.
func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) error {
self.updateTrie(db)
if self.dbErr != nil {
return self.dbErr
}
root, err := self.trie.CommitTo(dbw)
if err == nil {
self.data.Root = root
}
return err
}
// AddBalance removes amount from c's balance.
// It is used to add funds to the destination account of a transfer.
func (c *StateObject) AddBalance(amount *big.Int) {
c.SetBalance(new(big.Int).Add(c.balance, amount))
// EIP158: We must check emptiness for the objects such that the account
// clearing (0,0,0 objects) can take effect.
if amount.Cmp(common.Big0) == 0 && !c.empty() {
return
}
c.SetBalance(new(big.Int).Add(c.Balance(), amount))
if glog.V(logger.Core) {
glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.nonce, c.balance, amount)
glog.Infof("%x: #%d %v (+ %v)\n", c.Address(), c.Nonce(), c.Balance(), amount)
}
}
// SubBalance removes amount from c's balance.
// It is used to remove funds from the origin account of a transfer.
func (c *StateObject) SubBalance(amount *big.Int) {
c.SetBalance(new(big.Int).Sub(c.balance, amount))
if amount.Cmp(common.Big0) == 0 {
return
}
c.SetBalance(new(big.Int).Sub(c.Balance(), amount))
if glog.V(logger.Core) {
glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.nonce, c.balance, amount)
glog.Infof("%x: #%d %v (- %v)\n", c.Address(), c.Nonce(), c.Balance(), amount)
}
}
func (c *StateObject) SetBalance(amount *big.Int) {
c.balance = amount
c.dirty = true
func (self *StateObject) SetBalance(amount *big.Int) {
self.db.journal = append(self.db.journal, balanceChange{
account: &self.address,
prev: new(big.Int).Set(self.data.Balance),
})
self.setBalance(amount)
}
func (c *StateObject) St() Storage {
return c.storage
func (self *StateObject) setBalance(amount *big.Int) {
self.data.Balance = amount
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
// Return the gas back to the origin. Used by the Virtual machine or Closures
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
func (self *StateObject) Copy() *StateObject {
stateObject := NewStateObject(self.Address(), self.db)
stateObject.balance.Set(self.balance)
stateObject.codeHash = common.CopyBytes(self.codeHash)
stateObject.nonce = self.nonce
func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject {
stateObject := newObject(db, self.address, self.data, onDirty)
stateObject.trie = self.trie
stateObject.code = common.CopyBytes(self.code)
stateObject.initCode = common.CopyBytes(self.initCode)
stateObject.storage = self.storage.Copy()
stateObject.remove = self.remove
stateObject.dirty = self.dirty
stateObject.code = self.code
stateObject.dirtyStorage = self.dirtyStorage.Copy()
stateObject.cachedStorage = self.dirtyStorage.Copy()
stateObject.suicided = self.suicided
stateObject.dirtyCode = self.dirtyCode
stateObject.deleted = self.deleted
return stateObject
}
@@ -201,40 +289,73 @@ func (self *StateObject) Copy() *StateObject {
// Attribute accessors
//
func (self *StateObject) Balance() *big.Int {
return self.balance
}
// Returns the address of the contract/account
func (c *StateObject) Address() common.Address {
return c.address
}
func (self *StateObject) Trie() *trie.SecureTrie {
return self.trie
}
func (self *StateObject) Root() []byte {
return self.trie.Root()
}
func (self *StateObject) Code() []byte {
return self.code
}
func (self *StateObject) SetCode(code []byte) {
// Code returns the contract code associated with this object, if any.
func (self *StateObject) Code(db trie.Database) []byte {
if self.code != nil {
return self.code
}
if bytes.Equal(self.CodeHash(), emptyCodeHash) {
return nil
}
code, err := db.Get(self.CodeHash())
if err != nil {
self.setError(fmt.Errorf("can't load code hash %x: %v", self.CodeHash(), err))
}
self.code = code
self.codeHash = crypto.Keccak256(code)
self.dirty = true
return code
}
func (self *StateObject) SetCode(codeHash common.Hash, code []byte) {
prevcode := self.Code(self.db.db)
self.db.journal = append(self.db.journal, codeChange{
account: &self.address,
prevhash: self.CodeHash(),
prevcode: prevcode,
})
self.setCode(codeHash, code)
}
func (self *StateObject) setCode(codeHash common.Hash, code []byte) {
self.code = code
self.data.CodeHash = codeHash[:]
self.dirtyCode = true
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
func (self *StateObject) SetNonce(nonce uint64) {
self.nonce = nonce
self.dirty = true
self.db.journal = append(self.db.journal, nonceChange{
account: &self.address,
prev: self.data.Nonce,
})
self.setNonce(nonce)
}
func (self *StateObject) setNonce(nonce uint64) {
self.data.Nonce = nonce
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
func (self *StateObject) CodeHash() []byte {
return self.data.CodeHash
}
func (self *StateObject) Balance() *big.Int {
return self.data.Balance
}
func (self *StateObject) Nonce() uint64 {
return self.nonce
return self.data.Nonce
}
// Never called, but must be present to allow StateObject to be used
@@ -246,52 +367,16 @@ func (self *StateObject) Value() *big.Int {
func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
// When iterating over the storage check the cache first
for h, value := range self.storage {
for h, value := range self.cachedStorage {
cb(h, value)
}
it := self.trie.Iterator()
it := self.getTrie(self.db.db).Iterator()
for it.Next() {
// ignore cached values
key := common.BytesToHash(self.trie.GetKey(it.Key))
if _, ok := self.storage[key]; !ok {
if _, ok := self.cachedStorage[key]; !ok {
cb(key, common.BytesToHash(it.Value))
}
}
}
type extStateObject struct {
Nonce uint64
Balance *big.Int
Root common.Hash
CodeHash []byte
}
// EncodeRLP implements rlp.Encoder.
func (c *StateObject) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, []interface{}{c.nonce, c.balance, c.Root(), c.codeHash})
}
// DecodeObject decodes an RLP-encoded state object.
func DecodeObject(address common.Address, db trie.Database, data []byte) (*StateObject, error) {
var (
obj = &StateObject{address: address, db: db, storage: make(Storage)}
ext extStateObject
err error
)
if err = rlp.DecodeBytes(data, &ext); err != nil {
return nil, err
}
if obj.trie, err = trie.NewSecure(ext.Root, db); err != nil {
return nil, err
}
if !bytes.Equal(ext.CodeHash, emptyCodeHash) {
if obj.code, err = db.Get(ext.CodeHash); err != nil {
return nil, fmt.Errorf("can't get code for hash %x: %v", ext.CodeHash, err)
}
}
obj.nonce = ext.Nonce
obj.balance = ext.Balance
obj.codeHash = ext.CodeHash
return obj, nil
}

View File

@@ -24,6 +24,7 @@ import (
checker "gopkg.in/check.v1"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
)
@@ -40,14 +41,14 @@ func (s *StateSuite) TestDump(c *checker.C) {
obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01}))
obj1.AddBalance(big.NewInt(22))
obj2 := s.state.GetOrNewStateObject(toAddr([]byte{0x01, 0x02}))
obj2.SetCode([]byte{3, 3, 3, 3, 3, 3, 3})
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
obj3 := s.state.GetOrNewStateObject(toAddr([]byte{0x02}))
obj3.SetBalance(big.NewInt(44))
// write some of them to the trie
s.state.UpdateStateObject(obj1)
s.state.UpdateStateObject(obj2)
s.state.Commit()
s.state.updateStateObject(obj1)
s.state.updateStateObject(obj2)
s.state.Commit(false)
// check that dump contains the state objects that are in trie
got := string(s.state.Dump())
@@ -99,7 +100,7 @@ func TestNull(t *testing.T) {
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
var value common.Hash
state.SetState(address, common.Hash{}, value)
state.Commit()
state.Commit(false)
value = state.GetState(address, common.Hash{})
if !common.EmptyHash(value) {
t.Errorf("expected empty hash. got %x", value)
@@ -115,12 +116,12 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
// set initial state object value
s.state.SetState(stateobjaddr, storageaddr, data1)
// get snapshot of current state
snapshot := s.state.Copy()
snapshot := s.state.Snapshot()
// set new state object value
s.state.SetState(stateobjaddr, storageaddr, data2)
// restore snapshot
s.state.Set(snapshot)
s.state.RevertToSnapshot(snapshot)
// get state storage value
res := s.state.GetState(stateobjaddr, storageaddr)
@@ -128,6 +129,12 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
c.Assert(data1, checker.DeepEquals, res)
}
func TestSnapshotEmpty(t *testing.T) {
db, _ := ethdb.NewMemDatabase()
state, _ := New(common.Hash{}, db)
state.RevertToSnapshot(state.Snapshot())
}
// use testing instead of checker because checker does not support
// printing/logging in tests (-check.vv does not work)
func TestSnapshot2(t *testing.T) {
@@ -146,80 +153,85 @@ func TestSnapshot2(t *testing.T) {
// db, trie are already non-empty values
so0 := state.GetStateObject(stateobjaddr0)
so0.balance = big.NewInt(42)
so0.nonce = 43
so0.SetCode([]byte{'c', 'a', 'f', 'e'})
so0.remove = true
so0.SetBalance(big.NewInt(42))
so0.SetNonce(43)
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
so0.suicided = false
so0.deleted = false
so0.dirty = false
state.SetStateObject(so0)
state.setStateObject(so0)
root, _ := state.Commit(false)
state.Reset(root)
// and one with deleted == true
so1 := state.GetStateObject(stateobjaddr1)
so1.balance = big.NewInt(52)
so1.nonce = 53
so1.SetCode([]byte{'c', 'a', 'f', 'e', '2'})
so1.remove = true
so1.SetBalance(big.NewInt(52))
so1.SetNonce(53)
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
so1.suicided = true
so1.deleted = true
so1.dirty = true
state.SetStateObject(so1)
state.setStateObject(so1)
so1 = state.GetStateObject(stateobjaddr1)
if so1 != nil {
t.Fatalf("deleted object not nil when getting")
}
snapshot := state.Copy()
state.Set(snapshot)
snapshot := state.Snapshot()
state.RevertToSnapshot(snapshot)
so0Restored := state.GetStateObject(stateobjaddr0)
so1Restored := state.GetStateObject(stateobjaddr1)
// Update lazily-loaded values before comparing.
so0Restored.GetState(db, storageaddr)
so0Restored.Code(db)
// non-deleted is equal (restored)
compareStateObjects(so0Restored, so0, t)
// deleted should be nil, both before and after restore of state copy
so1Restored := state.GetStateObject(stateobjaddr1)
if so1Restored != nil {
t.Fatalf("deleted object not nil after restoring snapshot")
t.Fatalf("deleted object not nil after restoring snapshot: %+v", so1Restored)
}
}
func compareStateObjects(so0, so1 *StateObject, t *testing.T) {
if so0.address != so1.address {
if so0.Address() != so1.Address() {
t.Fatalf("Address mismatch: have %v, want %v", so0.address, so1.address)
}
if so0.balance.Cmp(so1.balance) != 0 {
t.Fatalf("Balance mismatch: have %v, want %v", so0.balance, so1.balance)
if so0.Balance().Cmp(so1.Balance()) != 0 {
t.Fatalf("Balance mismatch: have %v, want %v", so0.Balance(), so1.Balance())
}
if so0.nonce != so1.nonce {
t.Fatalf("Nonce mismatch: have %v, want %v", so0.nonce, so1.nonce)
if so0.Nonce() != so1.Nonce() {
t.Fatalf("Nonce mismatch: have %v, want %v", so0.Nonce(), so1.Nonce())
}
if !bytes.Equal(so0.codeHash, so1.codeHash) {
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.codeHash, so1.codeHash)
if so0.data.Root != so1.data.Root {
t.Errorf("Root mismatch: have %x, want %x", so0.data.Root[:], so1.data.Root[:])
}
if !bytes.Equal(so0.CodeHash(), so1.CodeHash()) {
t.Fatalf("CodeHash mismatch: have %v, want %v", so0.CodeHash(), so1.CodeHash())
}
if !bytes.Equal(so0.code, so1.code) {
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
}
if !bytes.Equal(so0.initCode, so1.initCode) {
t.Fatalf("InitCode mismatch: have %v, want %v", so0.initCode, so1.initCode)
}
for k, v := range so1.storage {
if so0.storage[k] != v {
t.Fatalf("Storage key %s mismatch: have %v, want %v", k, so0.storage[k], v)
if len(so1.cachedStorage) != len(so0.cachedStorage) {
t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage))
}
for k, v := range so1.cachedStorage {
if so0.cachedStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v)
}
}
for k, v := range so0.storage {
if so1.storage[k] != v {
t.Fatalf("Storage key %s mismatch: have %v, want none.", k, v)
for k, v := range so0.cachedStorage {
if so1.cachedStorage[k] != v {
t.Errorf("Storage key %x mismatch: have %v, want none.", k, v)
}
}
if so0.remove != so1.remove {
t.Fatalf("Remove mismatch: have %v, want %v", so0.remove, so1.remove)
if so0.suicided != so1.suicided {
t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided)
}
if so0.deleted != so1.deleted {
t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted)
}
if so0.dirty != so1.dirty {
t.Fatalf("Dirty mismatch: have %v, want %v", so0.dirty, so1.dirty)
}
}

View File

@@ -20,74 +20,157 @@ package state
import (
"fmt"
"math/big"
"sort"
"sync"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru"
)
// The starting nonce determines the default nonce when new accounts are being
// created.
var StartingNonce uint64
const (
// Number of past tries to keep. The arbitrarily chosen value here
// is max uncle depth + 1.
maxPastTries = 8
// Trie cache generation limit.
maxTrieCacheGen = 100
// Number of codehash->size associations to keep.
codeSizeCacheSize = 100000
)
type revision struct {
id int
journalIndex int
}
// StateDBs 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
// * Accounts
type StateDB struct {
db ethdb.Database
trie *trie.SecureTrie
db ethdb.Database
trie *trie.SecureTrie
pastTries []*trie.SecureTrie
codeSizeCache *lru.Cache
stateObjects map[string]*StateObject
// This map holds 'live' objects, which will get modified while processing a state transition.
stateObjects map[common.Address]*StateObject
stateObjectsDirty map[common.Address]struct{}
// The refund counter, also used by state transitioning.
refund *big.Int
thash, bhash common.Hash
txIndex int
logs map[common.Hash]vm.Logs
logSize uint
// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
journal journal
validRevisions []revision
nextRevisionId int
lock sync.Mutex
}
// Create a new state from a given trie
func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
tr, err := trie.NewSecure(root, db)
tr, err := trie.NewSecure(root, db, maxTrieCacheGen)
if err != nil {
return nil, err
}
csc, _ := lru.New(codeSizeCacheSize)
return &StateDB{
db: db,
trie: tr,
codeSizeCache: csc,
stateObjects: make(map[common.Address]*StateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
}, nil
}
// New creates a new statedb by reusing any journalled tries to avoid costly
// disk io.
func (self *StateDB) New(root common.Hash) (*StateDB, error) {
self.lock.Lock()
defer self.lock.Unlock()
tr, err := self.openTrie(root)
if err != nil {
return nil, err
}
return &StateDB{
db: db,
trie: tr,
stateObjects: make(map[string]*StateObject),
refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
db: self.db,
trie: tr,
codeSizeCache: self.codeSizeCache,
stateObjects: make(map[common.Address]*StateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
}, nil
}
// Reset clears out all emphemeral state objects from the state db, but keeps
// the underlying state trie to avoid reloading data for the next operations.
func (self *StateDB) Reset(root common.Hash) error {
var (
err error
tr = self.trie
)
if self.trie.Hash() != root {
if tr, err = trie.NewSecure(root, self.db); err != nil {
return err
self.lock.Lock()
defer self.lock.Unlock()
tr, err := self.openTrie(root)
if err != nil {
return err
}
self.trie = tr
self.stateObjects = make(map[common.Address]*StateObject)
self.stateObjectsDirty = make(map[common.Address]struct{})
self.thash = common.Hash{}
self.bhash = common.Hash{}
self.txIndex = 0
self.logs = make(map[common.Hash]vm.Logs)
self.logSize = 0
self.clearJournalAndRefund()
return nil
}
// openTrie creates a trie. It uses an existing trie if one is available
// from the journal if available.
func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) {
for i := len(self.pastTries) - 1; i >= 0; i-- {
if self.pastTries[i].Hash() == root {
tr := *self.pastTries[i]
return &tr, nil
}
}
*self = StateDB{
db: self.db,
trie: tr,
stateObjects: make(map[string]*StateObject),
refund: new(big.Int),
logs: make(map[common.Hash]vm.Logs),
return trie.NewSecure(root, self.db, maxTrieCacheGen)
}
func (self *StateDB) pushTrie(t *trie.SecureTrie) {
self.lock.Lock()
defer self.lock.Unlock()
if len(self.pastTries) >= maxPastTries {
copy(self.pastTries, self.pastTries[1:])
self.pastTries[len(self.pastTries)-1] = t
} else {
self.pastTries = append(self.pastTries, t)
}
return nil
}
func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
@@ -97,6 +180,8 @@ func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
}
func (self *StateDB) AddLog(log *vm.Log) {
self.journal = append(self.journal, addLogChange{txhash: self.thash})
log.TxHash = self.thash
log.BlockHash = self.bhash
log.TxIndex = uint(self.txIndex)
@@ -118,15 +203,19 @@ func (self *StateDB) Logs() vm.Logs {
}
func (self *StateDB) AddRefund(gas *big.Int) {
self.journal = append(self.journal, refundChange{prev: new(big.Int).Set(self.refund)})
self.refund.Add(self.refund, gas)
}
func (self *StateDB) HasAccount(addr common.Address) bool {
// Exist reports whether the given account address exists in the state.
// Notably this also returns true for suicided accounts.
func (self *StateDB) Exist(addr common.Address) bool {
return self.GetStateObject(addr) != nil
}
func (self *StateDB) Exist(addr common.Address) bool {
return self.GetStateObject(addr) != nil
func (self *StateDB) Empty(addr common.Address) bool {
so := self.GetStateObject(addr)
return so == nil || so.empty()
}
func (self *StateDB) GetAccount(addr common.Address) vm.Account {
@@ -137,16 +226,15 @@ func (self *StateDB) GetAccount(addr common.Address) vm.Account {
func (self *StateDB) GetBalance(addr common.Address) *big.Int {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.balance
return stateObject.Balance()
}
return common.Big0
}
func (self *StateDB) GetNonce(addr common.Address) uint64 {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.nonce
return stateObject.Nonce()
}
return StartingNonce
@@ -155,25 +243,50 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
func (self *StateDB) GetCode(addr common.Address) []byte {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.code
code := stateObject.Code(self.db)
key := common.BytesToHash(stateObject.CodeHash())
self.codeSizeCache.Add(key, len(code))
return code
}
return nil
}
func (self *StateDB) GetCodeSize(addr common.Address) int {
stateObject := self.GetStateObject(addr)
if stateObject == nil {
return 0
}
key := common.BytesToHash(stateObject.CodeHash())
if cached, ok := self.codeSizeCache.Get(key); ok {
return cached.(int)
}
size := len(stateObject.Code(self.db))
if stateObject.dbErr == nil {
self.codeSizeCache.Add(key, size)
}
return size
}
func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
stateObject := self.GetStateObject(addr)
if stateObject == nil {
return common.Hash{}
}
return common.BytesToHash(stateObject.CodeHash())
}
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
stateObject := self.GetStateObject(a)
if stateObject != nil {
return stateObject.GetState(b)
return stateObject.GetState(self.db, b)
}
return common.Hash{}
}
func (self *StateDB) IsDeleted(addr common.Address) bool {
func (self *StateDB) HasSuicided(addr common.Address) bool {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
return stateObject.remove
return stateObject.suicided
}
return false
}
@@ -189,6 +302,13 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
}
}
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetBalance(amount)
}
}
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
@@ -199,35 +319,43 @@ func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
func (self *StateDB) SetCode(addr common.Address, code []byte) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetCode(code)
stateObject.SetCode(crypto.Keccak256Hash(code), code)
}
}
func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) {
stateObject := self.GetOrNewStateObject(addr)
if stateObject != nil {
stateObject.SetState(key, value)
stateObject.SetState(self.db, key, value)
}
}
func (self *StateDB) Delete(addr common.Address) bool {
// Suicide marks the given account as suicided.
// This clears the account balance.
//
// The account's state object is still available until the state is committed,
// GetStateObject will return a non-nil account after Suicide.
func (self *StateDB) Suicide(addr common.Address) bool {
stateObject := self.GetStateObject(addr)
if stateObject != nil {
stateObject.MarkForDeletion()
stateObject.balance = new(big.Int)
return true
if stateObject == nil {
return false
}
return false
self.journal = append(self.journal, suicideChange{
account: &addr,
prev: stateObject.suicided,
prevbalance: new(big.Int).Set(stateObject.Balance()),
})
stateObject.markSuicided()
stateObject.data.Balance = new(big.Int)
return true
}
//
// Setting, updating & deleting state object methods
//
// Update the given state object and apply it to state trie
func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
// updateStateObject writes the given object to the trie.
func (self *StateDB) updateStateObject(stateObject *StateObject) {
addr := stateObject.Address()
data, err := rlp.EncodeToBytes(stateObject)
if err != nil {
@@ -236,117 +364,156 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
self.trie.Update(addr[:], data)
}
// Delete the given state object and delete it from the state trie
func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
// deleteStateObject removes the given object from the state trie.
func (self *StateDB) deleteStateObject(stateObject *StateObject) {
stateObject.deleted = true
addr := stateObject.Address()
self.trie.Delete(addr[:])
//delete(self.stateObjects, addr.Str())
}
// Retrieve a state object given my the address. Nil if not found
// Retrieve a state object given my the address. Returns nil if not found.
func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObject) {
stateObject = self.stateObjects[addr.Str()]
if stateObject != nil {
if stateObject.deleted {
stateObject = nil
// Prefer 'live' objects.
if obj := self.stateObjects[addr]; obj != nil {
if obj.deleted {
return nil
}
return stateObject
return obj
}
data := self.trie.Get(addr[:])
if len(data) == 0 {
// Load the object from the database.
enc := self.trie.Get(addr[:])
if len(enc) == 0 {
return nil
}
stateObject, err := DecodeObject(addr, self.db, data)
if err != nil {
var data Account
if err := rlp.DecodeBytes(enc, &data); err != nil {
glog.Errorf("can't decode object at %x: %v", addr[:], err)
return nil
}
self.SetStateObject(stateObject)
return stateObject
// Insert into the live set.
obj := newObject(self, addr, data, self.MarkStateObjectDirty)
self.setStateObject(obj)
return obj
}
func (self *StateDB) SetStateObject(object *StateObject) {
self.stateObjects[object.Address().Str()] = object
func (self *StateDB) setStateObject(object *StateObject) {
self.stateObjects[object.Address()] = object
}
// Retrieve a state object or create a new state object if nil
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
stateObject := self.GetStateObject(addr)
if stateObject == nil || stateObject.deleted {
stateObject = self.CreateStateObject(addr)
stateObject, _ = self.createObject(addr)
}
return stateObject
}
// NewStateObject create a state object whether it exist in the trie or not
func (self *StateDB) newStateObject(addr common.Address) *StateObject {
if glog.V(logger.Core) {
glog.Infof("(+) %x\n", addr)
}
stateObject := NewStateObject(addr, self.db)
stateObject.SetNonce(StartingNonce)
self.stateObjects[addr.Str()] = stateObject
return stateObject
// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
self.stateObjectsDirty[addr] = struct{}{}
}
// Creates creates a new state object and takes ownership. This is different from "NewStateObject"
func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
// Get previous (if any)
so := self.GetStateObject(addr)
// Create a new one
newSo := self.newStateObject(addr)
// If it existed set the balance to the new account
if so != nil {
newSo.balance = so.balance
// createObject creates a new state object. If there is an existing account with
// the given address, it is overwritten and returned as the second return value.
func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObject) {
prev = self.GetStateObject(addr)
newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty)
newobj.setNonce(StartingNonce) // sets the object to dirty
if prev == nil {
if glog.V(logger.Core) {
glog.Infof("(+) %x\n", addr)
}
self.journal = append(self.journal, createObjectChange{account: &addr})
} else {
self.journal = append(self.journal, resetObjectChange{prev: prev})
}
return newSo
self.setStateObject(newobj)
return newobj, prev
}
// CreateAccount explicitly creates a state object. If a state object with the address
// already exists the balance is carried over to the new account.
//
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
// a contract does the following:
//
// 1. sends funds to sha(account ++ (nonce + 1))
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
//
// Carrying over the balance ensures that Ether doesn't disappear.
func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
return self.CreateStateObject(addr)
new, prev := self.createObject(addr)
if prev != nil {
new.setBalance(prev.data.Balance)
}
return new
}
//
// Setting, copying of the state methods
//
// Copy creates a deep, independent copy of the state.
// Snapshots of the copied state cannot be applied to the copy.
func (self *StateDB) Copy() *StateDB {
// ignore error - we assume state-to-be-copied always exists
state, _ := New(common.Hash{}, self.db)
state.trie = self.trie
for k, stateObject := range self.stateObjects {
state.stateObjects[k] = stateObject.Copy()
self.lock.Lock()
defer self.lock.Unlock()
// Copy all the basic fields, initialize the memory ones
state := &StateDB{
db: self.db,
trie: self.trie,
pastTries: self.pastTries,
codeSizeCache: self.codeSizeCache,
stateObjects: make(map[common.Address]*StateObject, len(self.stateObjectsDirty)),
stateObjectsDirty: make(map[common.Address]struct{}, len(self.stateObjectsDirty)),
refund: new(big.Int).Set(self.refund),
logs: make(map[common.Hash]vm.Logs, len(self.logs)),
logSize: self.logSize,
}
// Copy the dirty states and logs
for addr, _ := range self.stateObjectsDirty {
state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty)
state.stateObjectsDirty[addr] = struct{}{}
}
state.refund.Set(self.refund)
for hash, logs := range self.logs {
state.logs[hash] = make(vm.Logs, len(logs))
copy(state.logs[hash], logs)
}
state.logSize = self.logSize
return state
}
func (self *StateDB) Set(state *StateDB) {
self.trie = state.trie
self.stateObjects = state.stateObjects
self.refund = state.refund
self.logs = state.logs
self.logSize = state.logSize
// Snapshot returns an identifier for the current revision of the state.
func (self *StateDB) Snapshot() int {
id := self.nextRevisionId
self.nextRevisionId++
self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)})
return id
}
// RevertToSnapshot reverts all state changes made since the given revision.
func (self *StateDB) RevertToSnapshot(revid int) {
// Find the snapshot in the stack of valid snapshots.
idx := sort.Search(len(self.validRevisions), func(i int) bool {
return self.validRevisions[i].id >= revid
})
if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
}
snapshot := self.validRevisions[idx].journalIndex
// Replay the journal to undo changes.
for i := len(self.journal) - 1; i >= snapshot; i-- {
self.journal[i].undo(self)
}
self.journal = self.journal[:snapshot]
// Remove invalidated snapshots from the stack.
self.validRevisions = self.validRevisions[:idx]
}
// GetRefund returns the current value of the refund counter.
// The return value must not be modified by the caller and will become
// invalid at the next call to AddRefund.
func (self *StateDB) GetRefund() *big.Int {
return self.refund
}
@@ -354,19 +521,18 @@ func (self *StateDB) GetRefund() *big.Int {
// IntermediateRoot computes the current root hash of the state trie.
// It is called in between transactions to get the root hash that
// goes into transaction receipts.
func (s *StateDB) IntermediateRoot() common.Hash {
s.refund = new(big.Int)
for _, stateObject := range s.stateObjects {
if stateObject.dirty {
if stateObject.remove {
s.DeleteStateObject(stateObject)
} else {
stateObject.Update()
s.UpdateStateObject(stateObject)
}
stateObject.dirty = false
func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
for addr, _ := range s.stateObjectsDirty {
stateObject := s.stateObjects[addr]
if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) {
s.deleteStateObject(stateObject)
} else {
stateObject.updateRoot(s.db)
s.updateStateObject(stateObject)
}
}
// Invalidate journal because reverting across transactions is not allowed.
s.clearJournalAndRefund()
return s.trie.Hash()
}
@@ -376,76 +542,75 @@ func (s *StateDB) IntermediateRoot() common.Hash {
// DeleteSuicides should not be used for consensus related updates
// under any circumstances.
func (s *StateDB) DeleteSuicides() {
// Reset refund so that any used-gas calculations can use
// this method.
s.refund = new(big.Int)
for _, stateObject := range s.stateObjects {
if stateObject.dirty {
// If the object has been removed by a suicide
// flag the object as deleted.
if stateObject.remove {
stateObject.deleted = true
}
stateObject.dirty = false
// Reset refund so that any used-gas calculations can use this method.
s.clearJournalAndRefund()
for addr, _ := range s.stateObjectsDirty {
stateObject := s.stateObjects[addr]
// If the object has been removed by a suicide
// flag the object as deleted.
if stateObject.suicided {
stateObject.deleted = true
}
delete(s.stateObjectsDirty, addr)
}
}
// Commit commits all state changes to the database.
func (s *StateDB) Commit() (root common.Hash, err error) {
root, batch := s.CommitBatch()
func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
root, batch := s.CommitBatch(deleteEmptyObjects)
return root, batch.Write()
}
// CommitBatch commits all state changes to a write batch but does not
// execute the batch. It is used to validate state changes against
// the root hash stored in a block.
func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) {
batch = s.db.NewBatch()
root, _ = s.commit(batch)
root, _ = s.commit(batch, deleteEmptyObjects)
return root, batch
}
func (s *StateDB) commit(db trie.DatabaseWriter) (common.Hash, error) {
func (s *StateDB) clearJournalAndRefund() {
s.journal = nil
s.validRevisions = s.validRevisions[:0]
s.refund = new(big.Int)
}
for _, stateObject := range s.stateObjects {
if stateObject.remove {
func (s *StateDB) commit(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) {
defer s.clearJournalAndRefund()
// Commit objects to the trie.
for addr, stateObject := range s.stateObjects {
_, isDirty := s.stateObjectsDirty[addr]
switch {
case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()):
// If the object has been removed, don't bother syncing it
// and just mark it for deletion in the trie.
s.DeleteStateObject(stateObject)
} else {
s.deleteStateObject(stateObject)
case isDirty:
// Write any contract code associated with the state object
if len(stateObject.code) > 0 {
if err := db.Put(stateObject.codeHash, stateObject.code); err != nil {
if stateObject.code != nil && stateObject.dirtyCode {
if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {
return common.Hash{}, err
}
stateObject.dirtyCode = false
}
// Write any storage changes in the state object to its trie.
stateObject.Update()
// Commit the trie of the object to the batch.
// This updates the trie root internally, so
// getting the root hash of the storage trie
// through UpdateStateObject is fast.
if _, err := stateObject.trie.CommitTo(db); err != nil {
// Write any storage changes in the state object to its storage trie.
if err := stateObject.CommitTrie(s.db, dbw); err != nil {
return common.Hash{}, err
}
// Update the object in the account trie.
s.UpdateStateObject(stateObject)
// Update the object in the main account trie.
s.updateStateObject(stateObject)
}
stateObject.dirty = false
delete(s.stateObjectsDirty, addr)
}
return s.trie.CommitTo(db)
}
func (self *StateDB) Refunds() *big.Int {
return self.refund
}
// Debug stuff
func (self *StateDB) CreateOutputForDiff() {
for _, stateObject := range self.stateObjects {
stateObject.CreateOutputForDiff()
// Write trie changes.
root, err = s.trie.CommitTo(dbw)
if err == nil {
s.pushTrie(s.trie)
}
return root, err
}

View File

@@ -17,10 +17,19 @@
package state
import (
"bytes"
"encoding/binary"
"fmt"
"math"
"math/big"
"math/rand"
"reflect"
"strings"
"testing"
"testing/quick"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
)
@@ -33,16 +42,16 @@ func TestUpdateLeaks(t *testing.T) {
// Update it with some accounts
for i := byte(0); i < 255; i++ {
obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.AddBalance(big.NewInt(int64(11 * i)))
obj.SetNonce(uint64(42 * i))
addr := common.BytesToAddress([]byte{i})
state.AddBalance(addr, big.NewInt(int64(11*i)))
state.SetNonce(addr, uint64(42*i))
if i%2 == 0 {
obj.SetState(common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
}
if i%3 == 0 {
obj.SetCode([]byte{i, i, i, i, i})
state.SetCode(addr, []byte{i, i, i, i, i})
}
state.UpdateStateObject(obj)
state.IntermediateRoot(false)
}
// Ensure that no data was leaked into the database
for _, key := range db.Keys() {
@@ -60,51 +69,38 @@ func TestIntermediateLeaks(t *testing.T) {
transState, _ := New(common.Hash{}, transDb)
finalState, _ := New(common.Hash{}, finalDb)
// Update the states with some objects
for i := byte(0); i < 255; i++ {
// Create a new state object with some data into the transition database
obj := transState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.SetBalance(big.NewInt(int64(11 * i)))
obj.SetNonce(uint64(42 * i))
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)))
state.SetNonce(addr, uint64(42*i+tweak))
if i%2 == 0 {
obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.BytesToHash([]byte{i, i, i, i, 0}))
state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{})
state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak})
}
if i%3 == 0 {
obj.SetCode([]byte{i, i, i, i, i, 0})
state.SetCode(addr, []byte{i, i, i, i, i, tweak})
}
transState.UpdateStateObject(obj)
// Overwrite all the data with new values in the transition database
obj.SetBalance(big.NewInt(int64(11*i + 1)))
obj.SetNonce(uint64(42*i + 1))
if i%2 == 0 {
obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.Hash{})
obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
}
if i%3 == 0 {
obj.SetCode([]byte{i, i, i, i, i, 1})
}
transState.UpdateStateObject(obj)
// Create the final state object directly in the final database
obj = finalState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
obj.SetBalance(big.NewInt(int64(11*i + 1)))
obj.SetNonce(uint64(42*i + 1))
if i%2 == 0 {
obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
}
if i%3 == 0 {
obj.SetCode([]byte{i, i, i, i, i, 1})
}
finalState.UpdateStateObject(obj)
}
if _, err := transState.Commit(); err != nil {
// Modify the transient state.
for i := byte(0); i < 255; i++ {
modify(transState, common.Address{byte(i)}, i, 0)
}
// Write modifications to trie.
transState.IntermediateRoot(false)
// Overwrite all the data with new values in the transient database.
for i := byte(0); i < 255; i++ {
modify(transState, common.Address{byte(i)}, i, 99)
modify(finalState, common.Address{byte(i)}, i, 99)
}
// Commit and cross check the databases.
if _, err := transState.Commit(false); err != nil {
t.Fatalf("failed to commit transition state: %v", err)
}
if _, err := finalState.Commit(); err != nil {
if _, err := finalState.Commit(false); err != nil {
t.Fatalf("failed to commit final state: %v", err)
}
// Cross check the databases to ensure they are the same
for _, key := range finalDb.Keys() {
if _, err := transDb.Get(key); err != nil {
val, _ := finalDb.Get(key)
@@ -118,3 +114,243 @@ func TestIntermediateLeaks(t *testing.T) {
}
}
}
func TestSnapshotRandom(t *testing.T) {
config := &quick.Config{MaxCount: 1000}
err := quick.Check((*snapshotTest).run, config)
if cerr, ok := err.(*quick.CheckError); ok {
test := cerr.In[0].(*snapshotTest)
t.Errorf("%v:\n%s", test.err, test)
} else if err != nil {
t.Error(err)
}
}
// A snapshotTest checks that reverting StateDB snapshots properly undoes all changes
// captured by the snapshot. Instances of this test with pseudorandom content are created
// by Generate.
//
// The test works as follows:
//
// A new state is created and all actions are applied to it. Several snapshots are taken
// in between actions. The test then reverts each snapshot. For each snapshot the actions
// leading up to it are replayed on a fresh, empty state. The behaviour of all public
// accessor methods on the reverted state must match the return value of the equivalent
// methods on the replayed state.
type snapshotTest struct {
addrs []common.Address // all account addresses
actions []testAction // modifications to the state
snapshots []int // actions indexes at which snapshot is taken
err error // failure details are reported through this field
}
type testAction struct {
name string
fn func(testAction, *StateDB)
args []int64
noAddr bool
}
// newTestAction creates a random action that changes state.
func newTestAction(addr common.Address, r *rand.Rand) testAction {
actions := []testAction{
{
name: "SetBalance",
fn: func(a testAction, s *StateDB) {
s.SetBalance(addr, big.NewInt(a.args[0]))
},
args: make([]int64, 1),
},
{
name: "AddBalance",
fn: func(a testAction, s *StateDB) {
s.AddBalance(addr, big.NewInt(a.args[0]))
},
args: make([]int64, 1),
},
{
name: "SetNonce",
fn: func(a testAction, s *StateDB) {
s.SetNonce(addr, uint64(a.args[0]))
},
args: make([]int64, 1),
},
{
name: "SetState",
fn: func(a testAction, s *StateDB) {
var key, val common.Hash
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
s.SetState(addr, key, val)
},
args: make([]int64, 2),
},
{
name: "SetCode",
fn: func(a testAction, s *StateDB) {
code := make([]byte, 16)
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
s.SetCode(addr, code)
},
args: make([]int64, 2),
},
{
name: "CreateAccount",
fn: func(a testAction, s *StateDB) {
s.CreateAccount(addr)
},
},
{
name: "Suicide",
fn: func(a testAction, s *StateDB) {
s.Suicide(addr)
},
},
{
name: "AddRefund",
fn: func(a testAction, s *StateDB) {
s.AddRefund(big.NewInt(a.args[0]))
},
args: make([]int64, 1),
noAddr: true,
},
{
name: "AddLog",
fn: func(a testAction, s *StateDB) {
data := make([]byte, 2)
binary.BigEndian.PutUint16(data, uint16(a.args[0]))
s.AddLog(&vm.Log{Address: addr, Data: data})
},
args: make([]int64, 1),
},
}
action := actions[r.Intn(len(actions))]
var nameargs []string
if !action.noAddr {
nameargs = append(nameargs, addr.Hex())
}
for _, i := range action.args {
action.args[i] = rand.Int63n(100)
nameargs = append(nameargs, fmt.Sprint(action.args[i]))
}
action.name += strings.Join(nameargs, ", ")
return action
}
// Generate returns a new snapshot test of the given size. All randomness is
// derived from r.
func (*snapshotTest) Generate(r *rand.Rand, size int) reflect.Value {
// Generate random actions.
addrs := make([]common.Address, 50)
for i := range addrs {
addrs[i][0] = byte(i)
}
actions := make([]testAction, size)
for i := range actions {
addr := addrs[r.Intn(len(addrs))]
actions[i] = newTestAction(addr, r)
}
// Generate snapshot indexes.
nsnapshots := int(math.Sqrt(float64(size)))
if size > 0 && nsnapshots == 0 {
nsnapshots = 1
}
snapshots := make([]int, nsnapshots)
snaplen := len(actions) / nsnapshots
for i := range snapshots {
// Try to place the snapshots some number of actions apart from each other.
snapshots[i] = (i * snaplen) + r.Intn(snaplen)
}
return reflect.ValueOf(&snapshotTest{addrs, actions, snapshots, nil})
}
func (test *snapshotTest) String() string {
out := new(bytes.Buffer)
sindex := 0
for i, action := range test.actions {
if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
fmt.Fprintf(out, "---- snapshot %d ----\n", sindex)
sindex++
}
fmt.Fprintf(out, "%4d: %s\n", i, action.name)
}
return out.String()
}
func (test *snapshotTest) run() bool {
// Run all actions and create snapshots.
var (
db, _ = ethdb.NewMemDatabase()
state, _ = New(common.Hash{}, db)
snapshotRevs = make([]int, len(test.snapshots))
sindex = 0
)
for i, action := range test.actions {
if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
snapshotRevs[sindex] = state.Snapshot()
sindex++
}
action.fn(action, state)
}
// Revert all snapshots in reverse order. Each revert must yield a state
// that is equivalent to fresh state with all actions up the snapshot applied.
for sindex--; sindex >= 0; sindex-- {
checkstate, _ := New(common.Hash{}, db)
for _, action := range test.actions[:test.snapshots[sindex]] {
action.fn(action, checkstate)
}
state.RevertToSnapshot(snapshotRevs[sindex])
if err := test.checkEqual(state, checkstate); err != nil {
test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err)
return false
}
}
return true
}
// checkEqual checks that methods of state and checkstate return the same values.
func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
for _, addr := range test.addrs {
var err error
checkeq := func(op string, a, b interface{}) bool {
if err == nil && !reflect.DeepEqual(a, b) {
err = fmt.Errorf("got %s(%s) == %v, want %v", op, addr.Hex(), a, b)
return false
}
return true
}
// Check basic accessor methods.
checkeq("Exist", state.Exist(addr), checkstate.Exist(addr))
checkeq("HasSuicided", state.HasSuicided(addr), checkstate.HasSuicided(addr))
checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr))
checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr))
checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr))
checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr))
checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
// Check storage.
if obj := state.GetStateObject(addr); obj != nil {
obj.ForEachStorage(func(key, val common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key))
})
checkobj := checkstate.GetStateObject(addr)
checkobj.ForEachStorage(func(key, checkval common.Hash) bool {
return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval)
})
}
if err != nil {
return err
}
}
if state.GetRefund().Cmp(checkstate.GetRefund()) != 0 {
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
state.GetRefund(), checkstate.GetRefund())
}
if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) {
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{}))
}
return nil
}

View File

@@ -54,16 +54,13 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
acc.nonce = uint64(42 * i)
if i%3 == 0 {
obj.SetCode([]byte{i, i, i, i, i})
obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i})
acc.code = []byte{i, i, i, i, i}
}
state.UpdateStateObject(obj)
state.updateStateObject(obj)
accounts = append(accounts, acc)
}
root, _ := state.Commit()
// Remove any potentially cached data from the test state creation
trie.ClearGlobalCache()
root, _ := state.Commit(false)
// Return the generated state
return db, root, accounts
@@ -72,9 +69,6 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
// checkStateAccounts cross references a reconstructed state with an expected
// account array.
func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accounts []*testAccount) {
// Remove any potentially cached data from the state synchronisation
trie.ClearGlobalCache()
// Check root availability and state contents
state, err := New(root, db)
if err != nil {
@@ -98,9 +92,6 @@ func checkStateAccounts(t *testing.T, db ethdb.Database, root common.Hash, accou
// checkStateConsistency checks that all nodes in a state trie are indeed present.
func checkStateConsistency(db ethdb.Database, root common.Hash) error {
// Remove any potentially cached data from the test state creation or previous checks
trie.ClearGlobalCache()
// Create and iterate a state trie rooted in a sub-node
if _, err := db.Get(root.Bytes()); err != nil {
return nil // Consider a non existent state consistent

View File

@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
)
var (
@@ -37,12 +38,12 @@ var (
//
// StateProcessor implements Processor.
type StateProcessor struct {
config *ChainConfig
config *params.ChainConfig
bc *BlockChain
}
// NewStateProcessor initialises a new StateProcessor.
func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor {
func NewStateProcessor(config *params.ChainConfig, bc *BlockChain) *StateProcessor {
return &StateProcessor{
config: config,
bc: bc,
@@ -89,7 +90,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
//
// ApplyTransactions returns the generated receipts and vm logs during the
// execution of the state transition phase.
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
var signer types.Signer
switch {
case config.IsEIP155(header.Number):
signer = types.NewEIP155Signer(config.ChainId)
case config.IsHomestead(header.Number):
signer = types.HomesteadSigner{}
default:
signer = types.FrontierSigner{}
}
tx.SetSigner(signer)
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
if err != nil {
return nil, nil, nil, err
@@ -97,7 +109,7 @@ func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb
// Update the state with pending changes
usedGas.Add(usedGas, gas)
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)
receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas)
receipt.TxHash = tx.Hash()
receipt.GasUsed = new(big.Int).Set(gas)
if MessageCreatesContract(tx) {

View File

@@ -138,7 +138,7 @@ func (self *StateTransition) from() (vm.Account, error) {
f common.Address
err error
)
if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) {
if self.env.ChainConfig().IsHomestead(self.env.BlockNumber()) {
f, err = self.msg.From()
} else {
f, err = self.msg.FromFrontier()
@@ -231,7 +231,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
msg := self.msg
sender, _ := self.from() // err checked in preCheck
homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber())
homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber())
contractCreation := MessageCreatesContract(msg)
// Pay intrinsic gas
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {

342
core/tx_list.go Normal file
View File

@@ -0,0 +1,342 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"container/heap"
"math"
"math/big"
"sort"
"github.com/ethereum/go-ethereum/core/types"
)
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
// retrieving sorted transactions from the possibly gapped future queue.
type nonceHeap []uint64
func (h nonceHeap) Len() int { return len(h) }
func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] }
func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
func (h *nonceHeap) Push(x interface{}) {
*h = append(*h, x.(uint64))
}
func (h *nonceHeap) Pop() interface{} {
old := *h
n := len(old)
x := old[n-1]
*h = old[0 : n-1]
return x
}
// txSortedMap is a nonce->transaction hash map with a heap based index to allow
// iterating over the contents in a nonce-incrementing way.
type txSortedMap struct {
items map[uint64]*types.Transaction // Hash map storing the transaction data
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
cache types.Transactions // Cache of the transactions already sorted
}
// newTxSortedMap creates a new sorted transaction map.
func newTxSortedMap() *txSortedMap {
return &txSortedMap{
items: make(map[uint64]*types.Transaction),
index: &nonceHeap{},
}
}
// Get retrieves the current transactions associated with the given nonce.
func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
return m.items[nonce]
}
// Put inserts a new transaction into the map, also updating the map's nonce
// index. If a transaction already exists with the same nonce, it's overwritten.
func (m *txSortedMap) Put(tx *types.Transaction) {
nonce := tx.Nonce()
if m.items[nonce] == nil {
heap.Push(m.index, nonce)
}
m.items[nonce], m.cache = tx, nil
}
// Forward removes all transactions from the map with a nonce lower than the
// provided threshold. Every removed transaction is returned for any post-removal
// maintenance.
func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
var removed types.Transactions
// Pop off heap items until the threshold is reached
for m.index.Len() > 0 && (*m.index)[0] < threshold {
nonce := heap.Pop(m.index).(uint64)
removed = append(removed, m.items[nonce])
delete(m.items, nonce)
}
// If we had a cached order, shift the front
if m.cache != nil {
m.cache = m.cache[len(removed):]
}
return removed
}
// Filter iterates over the list of transactions and removes all of them for which
// the specified function evaluates to true.
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
var removed types.Transactions
// Collect all the transactions to filter out
for nonce, tx := range m.items {
if filter(tx) {
removed = append(removed, tx)
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
}
// Cap places a hard limit on the number of items, returning all transactions
// exceeding that limit.
func (m *txSortedMap) Cap(threshold int) types.Transactions {
// Short circuit if the number of items is under the limit
if len(m.items) <= threshold {
return nil
}
// Otherwise gather and drop the highest nonce'd transactions
var drops types.Transactions
sort.Sort(*m.index)
for size := len(m.items); size > threshold; size-- {
drops = append(drops, m.items[(*m.index)[size-1]])
delete(m.items, (*m.index)[size-1])
}
*m.index = (*m.index)[:threshold]
heap.Init(m.index)
// If we had a cache, shift the back
if m.cache != nil {
m.cache = m.cache[:len(m.cache)-len(drops)]
}
return drops
}
// Remove deletes a transaction from the maintained map, returning whether the
// transaction was found.
func (m *txSortedMap) Remove(nonce uint64) bool {
// Short circuit if no transaction is present
_, ok := m.items[nonce]
if !ok {
return false
}
// Otherwise delete the transaction and fix the heap index
for i := 0; i < m.index.Len(); i++ {
if (*m.index)[i] == nonce {
heap.Remove(m.index, i)
break
}
}
delete(m.items, nonce)
m.cache = nil
return true
}
// Ready retrieves a sequentially increasing list of transactions starting at the
// provided nonce that is ready for processing. The returned transactions will be
// removed from the list.
//
// Note, all transactions with nonces lower than start will also be returned to
// prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing!
func (m *txSortedMap) Ready(start uint64) types.Transactions {
// Short circuit if no transactions are available
if m.index.Len() == 0 || (*m.index)[0] > start {
return nil
}
// Otherwise start accumulating incremental transactions
var ready types.Transactions
for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ {
ready = append(ready, m.items[next])
delete(m.items, next)
heap.Pop(m.index)
}
m.cache = nil
return ready
}
// Len returns the length of the transaction map.
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 {
// If the sorting was not cached yet, create and cache it
if m.cache == nil {
m.cache = make(types.Transactions, 0, len(m.items))
for _, tx := range m.items {
m.cache = append(m.cache, tx)
}
sort.Sort(types.TxByNonce(m.cache))
}
// Copy the cache to prevent accidental modifications
txs := make(types.Transactions, len(m.cache))
copy(txs, m.cache)
return txs
}
// 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-
// executable/future queue, with minor behavoiral changes.
type txList struct {
strict bool // Whether nonces are strictly continuous or not
txs *txSortedMap // Heap indexed sorted hash map of the transactions
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
}
// newTxList create a new transaction list for maintaining nonce-indexable fast,
// gapped, sortable transaction lists.
func newTxList(strict bool) *txList {
return &txList{
strict: strict,
txs: newTxSortedMap(),
costcap: new(big.Int),
}
}
// Add tries to insert a new transaction into the list, returning whether the
// transaction was accepted, and if yes, any previous transaction it replaced.
//
// If the new transaction is accepted into the list, the lists' cost threshold
// is also potentially updated.
func (l *txList) Add(tx *types.Transaction) (bool, *types.Transaction) {
// If there's an older better transaction, abort
old := l.txs.Get(tx.Nonce())
if old != nil && old.GasPrice().Cmp(tx.GasPrice()) >= 0 {
return false, nil
}
// Otherwise overwrite the old transaction with the current one
l.txs.Put(tx)
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
l.costcap = cost
}
return true, old
}
// Forward removes all transactions from the list with a nonce lower than the
// provided threshold. Every removed transaction is returned for any post-removal
// maintenance.
func (l *txList) Forward(threshold uint64) types.Transactions {
return l.txs.Forward(threshold)
}
// Filter removes all transactions from the list with a cost higher than the
// provided threshold. Every removed transaction is returned for any post-removal
// maintenance. Strict-mode invalidated transactions are also returned.
//
// This method uses the cached costcap to quickly decide if there's even a point
// in calculating all the costs or if the balance covers all. If the threshold is
// lower than the costcap, the costcap will be reset to a new high after removing
// expensive the too transactions.
func (l *txList) Filter(threshold *big.Int) (types.Transactions, types.Transactions) {
// If all transactions are below the threshold, short circuit
if l.costcap.Cmp(threshold) <= 0 {
return nil, nil
}
l.costcap = new(big.Int).Set(threshold) // Lower the cap to the threshold
// Filter out all the transactions above the account's funds
removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(threshold) > 0 })
// If the list was strict, filter anything above the lowest nonce
var invalids types.Transactions
if l.strict && len(removed) > 0 {
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 })
}
return removed, invalids
}
// Cap places a hard limit on the number of items, returning all transactions
// exceeding that limit.
func (l *txList) Cap(threshold int) types.Transactions {
return l.txs.Cap(threshold)
}
// Remove deletes a transaction from the maintained list, returning whether the
// transaction was found, and also returning any transaction invalidated due to
// the deletion (strict mode only).
func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
// Remove the transaction from the set
nonce := tx.Nonce()
if removed := l.txs.Remove(nonce); !removed {
return false, nil
}
// In strict mode, filter out non-executable transactions
if l.strict {
return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce })
}
return true, nil
}
// Ready retrieves a sequentially increasing list of transactions starting at the
// provided nonce that is ready for processing. The returned transactions will be
// removed from the list.
//
// Note, all transactions with nonces lower than start will also be returned to
// prevent getting into and invalid state. This is not something that should ever
// happen but better to be self correcting than failing!
func (l *txList) Ready(start uint64) types.Transactions {
return l.txs.Ready(start)
}
// Len returns the length of the transaction list.
func (l *txList) Len() int {
return l.txs.Len()
}
// Empty returns whether the list of transactions is empty or not.
func (l *txList) Empty() bool {
return l.Len() == 0
}
// 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 (l *txList) Flatten() types.Transactions {
return l.txs.Flatten()
}

52
core/tx_list_test.go Normal file
View File

@@ -0,0 +1,52 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package core
import (
"math/big"
"math/rand"
"testing"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
)
// Tests that transactions can be added to strict lists and list contents and
// nonce boundaries are correctly maintained.
func TestStrictTxListAdd(t *testing.T) {
// Generate a list of transactions to insert
key, _ := crypto.GenerateKey()
txs := make(types.Transactions, 1024)
for i := 0; i < len(txs); i++ {
txs[i] = transaction(uint64(i), new(big.Int), key)
}
// Insert the transactions in a random order
list := newTxList(true)
for _, v := range rand.Perm(len(txs)) {
list.Add(txs[v])
}
// Verify internal state
if len(list.txs.items) != len(txs) {
t.Errorf("transaction count mismatch: have %d, want %d", len(list.txs.items), len(txs))
}
for i, tx := range txs {
if list.txs.items[tx.Nonce()] != tx {
t.Errorf("item %d: transaction mismatch: have %v, want %v", i, list.txs.items[tx.Nonce()], tx)
}
}
}

View File

@@ -30,6 +30,8 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/params"
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
)
var (
@@ -45,8 +47,13 @@ var (
ErrNegativeValue = errors.New("Negative value")
)
const (
maxQueued = 64 // max limit of queued txs per address
var (
minPendingPerAccount = uint64(16) // Min number of guaranteed transaction slots per address
maxPendingTotal = uint64(4096) // Max limit of pending transactions from all accounts (soft)
maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address
maxQueuedInTotal = uint64(1024) // Max limit of queued transactions from all accounts
maxQueuedLifetime = 3 * time.Hour // Max amount of time transactions from idle accounts are queued
evictionInterval = time.Minute // Time interval to check for evictable transactions
)
type stateFn func() (*state.StateDB, error)
@@ -59,7 +66,9 @@ type stateFn func() (*state.StateDB, error)
// current state) and future transactions. Transactions move between those
// two states over time as they are received and processed.
type TxPool struct {
config *ChainConfig
config *params.ChainConfig
signer types.Signer
currentState stateFn // The state function which will allow us to do some pre checks
pendingState *state.ManagedState
gasLimit func() *big.Int // The current gas limit function callback
@@ -68,19 +77,26 @@ type TxPool struct {
events event.Subscription
localTx *txSet
mu sync.RWMutex
pending map[common.Hash]*types.Transaction // processable transactions
queue map[common.Address]map[common.Hash]*types.Transaction
wg sync.WaitGroup // for shutdown sync
pending map[common.Address]*txList // All currently processable transactions
queue map[common.Address]*txList // Queued but non-processable transactions
all map[common.Hash]*types.Transaction // All transactions to allow lookups
beats map[common.Address]time.Time // Last heartbeat from each known account
wg sync.WaitGroup // for shutdown sync
quit chan struct{}
homestead bool
}
func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
pool := &TxPool{
config: config,
pending: make(map[common.Hash]*types.Transaction),
queue: make(map[common.Address]map[common.Hash]*types.Transaction),
signer: types.NewEIP155Signer(config.ChainId),
pending: make(map[common.Address]*txList),
queue: make(map[common.Address]*txList),
all: make(map[common.Hash]*types.Transaction),
beats: make(map[common.Address]time.Time),
eventMux: eventMux,
currentState: currentStateFn,
gasLimit: gasLimitFn,
@@ -88,10 +104,12 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat
pendingState: nil,
localTx: newTxSet(),
events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}),
quit: make(chan struct{}),
}
pool.wg.Add(1)
pool.wg.Add(2)
go pool.eventLoop()
go pool.expirationLoop()
return pool
}
@@ -117,7 +135,7 @@ func (pool *TxPool) eventLoop() {
pool.minGasPrice = ev.Price
pool.mu.Unlock()
case RemovedTransactionEvent:
pool.AddTransactions(ev.Txs)
pool.AddBatch(ev.Txs)
}
}
}
@@ -125,12 +143,12 @@ func (pool *TxPool) eventLoop() {
func (pool *TxPool) resetState() {
currentState, err := pool.currentState()
if err != nil {
glog.V(logger.Info).Infoln("failed to get current state: %v", err)
glog.V(logger.Error).Infof("Failed to get current state: %v", err)
return
}
managedState := state.ManageState(currentState)
if err != nil {
glog.V(logger.Info).Infoln("failed to get managed state: %v", err)
glog.V(logger.Error).Infof("Failed to get managed state: %v", err)
return
}
pool.pendingState = managedState
@@ -139,26 +157,21 @@ func (pool *TxPool) resetState() {
// any transactions that have been included in the block or
// have been invalidated because of another transaction (e.g.
// higher gas price)
pool.validatePool()
pool.demoteUnexecutables()
// Loop over the pending transactions and base the nonce of the new
// pending transaction set.
for _, tx := range pool.pending {
if addr, err := tx.From(); err == nil {
// Set the nonce. Transaction nonce can never be lower
// than the state nonce; validatePool took care of that.
if pool.pendingState.GetNonce(addr) <= tx.Nonce() {
pool.pendingState.SetNonce(addr, tx.Nonce()+1)
}
}
// 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.pendingState.SetNonce(addr, txs[len(txs)-1].Nonce()+1)
}
// Check the queue and move transactions over to the pending if possible
// or remove those that have become invalid
pool.checkQueue()
pool.promoteExecutables()
}
func (pool *TxPool) Stop() {
pool.events.Unsubscribe()
close(pool.quit)
pool.wg.Wait()
glog.V(logger.Info).Infoln("Transaction pool stopped")
}
@@ -170,47 +183,58 @@ func (pool *TxPool) State() *state.ManagedState {
return pool.pendingState
}
// Stats retrieves the current pool stats, namely the number of pending and the
// number of queued (non-executable) transactions.
func (pool *TxPool) Stats() (pending int, queued int) {
pool.mu.RLock()
defer pool.mu.RUnlock()
pending = len(pool.pending)
for _, txs := range pool.queue {
queued += len(txs)
for _, list := range pool.pending {
pending += list.Len()
}
for _, list := range pool.queue {
queued += list.Len()
}
return
}
// Content retrieves the data content of the transaction pool, returning all the
// pending as well as queued transactions, grouped by account and nonce.
func (pool *TxPool) Content() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) {
// pending as well as queued transactions, grouped by account and sorted by nonce.
func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
pool.mu.RLock()
defer pool.mu.RUnlock()
// Retrieve all the pending transactions and sort by account and by nonce
pending := make(map[common.Address]map[uint64][]*types.Transaction)
for _, tx := range pool.pending {
account, _ := tx.From()
owned, ok := pending[account]
if !ok {
owned = make(map[uint64][]*types.Transaction)
pending[account] = owned
}
owned[tx.Nonce()] = append(owned[tx.Nonce()], tx)
pending := make(map[common.Address]types.Transactions)
for addr, list := range pool.pending {
pending[addr] = list.Flatten()
}
// Retrieve all the queued transactions and sort by account and by nonce
queued := make(map[common.Address]map[uint64][]*types.Transaction)
for account, txs := range pool.queue {
owned := make(map[uint64][]*types.Transaction)
for _, tx := range txs {
owned[tx.Nonce()] = append(owned[tx.Nonce()], tx)
}
queued[account] = owned
queued := make(map[common.Address]types.Transactions)
for addr, list := range pool.queue {
queued[addr] = list.Flatten()
}
return pending, queued
}
// Pending retrieves all currently processable transactions, groupped by origin
// account and sorted by nonce. The returned transaction set is a copy and can be
// freely modified by calling code.
func (pool *TxPool) Pending() map[common.Address]types.Transactions {
pool.mu.Lock()
defer pool.mu.Unlock()
// check queue first
pool.promoteExecutables()
// invalidate any txs
pool.demoteUnexecutables()
pending := make(map[common.Address]types.Transactions)
for addr, list := range pool.pending {
pending[addr] = list.Flatten()
}
return pending
}
// SetLocal marks a transaction as local, skipping gas price
// check against local miner minimum in the future
func (pool *TxPool) SetLocal(tx *types.Transaction) {
@@ -233,6 +257,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return err
}
tx.SetSigner(pool.signer)
from, err := tx.From()
if err != nil {
return ErrInvalidSender
@@ -240,7 +265,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
// Make sure the account exist. Non existent accounts
// haven't got funds and well therefor never pass.
if !currentState.HasAccount(from) {
if !currentState.Exist(from) {
return ErrNonExistentAccount
}
@@ -276,312 +301,400 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
return nil
}
// validate and queue transactions.
func (self *TxPool) add(tx *types.Transaction) error {
// add validates a transaction and inserts it into the non-executable queue for
// later pending promotion and execution.
func (pool *TxPool) add(tx *types.Transaction) error {
// If the transaction is alreayd known, discard it
hash := tx.Hash()
if self.pending[hash] != nil {
return fmt.Errorf("Known transaction (%x)", hash[:4])
if pool.all[hash] != nil {
return fmt.Errorf("Known transaction: %x", hash[:4])
}
err := self.validateTx(tx)
if err != nil {
// Otherwise ensure basic validation passes and queue it up
if err := pool.validateTx(tx); err != nil {
return err
}
self.queueTx(hash, tx)
pool.enqueueTx(hash, tx)
// Print a log message if low enough level is set
if glog.V(logger.Debug) {
var toname string
rcpt := "[NEW_CONTRACT]"
if to := tx.To(); to != nil {
toname = common.Bytes2Hex(to[:4])
} else {
toname = "[NEW_CONTRACT]"
rcpt = common.Bytes2Hex(to[:4])
}
// we can ignore the error here because From is
// verified in ValidateTransaction.
f, _ := tx.From()
from := common.Bytes2Hex(f[:4])
glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash)
from, _ := tx.From() // from already verified during tx validation
glog.Infof("(t) 0x%x => %s (%v) %x\n", from[:4], rcpt, tx.Value, hash)
}
return nil
}
// queueTx will queue an unknown transaction
func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) {
// enqueueTx inserts a new transaction into the non-executable transaction queue.
//
// Note, this method assumes the pool lock is held!
func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) {
// Try to insert the transaction into the future queue
from, _ := tx.From() // already validated
if self.queue[from] == nil {
self.queue[from] = make(map[common.Hash]*types.Transaction)
if pool.queue[from] == nil {
pool.queue[from] = newTxList(false)
}
self.queue[from][hash] = tx
inserted, old := pool.queue[from].Add(tx)
if !inserted {
return // An older transaction was better, discard this
}
// Discard any previous transaction and mark this
if old != nil {
delete(pool.all, old.Hash())
}
pool.all[hash] = tx
}
// addTx will add a transaction to the pending (processable queue) list of transactions
func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) {
// init delayed since tx pool could have been started before any state sync
// promoteTx adds a transaction to the pending (processable) list of transactions.
//
// Note, this method assumes the pool lock is held!
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) {
// Init delayed since tx pool could have been started before any state sync
if pool.pendingState == nil {
pool.resetState()
}
if _, ok := pool.pending[hash]; !ok {
pool.pending[hash] = tx
// Increment the nonce on the pending state. This can only happen if
// the nonce is +1 to the previous one.
pool.pendingState.SetNonce(addr, tx.Nonce()+1)
// Notify the subscribers. This event is posted in a goroutine
// because it's possible that somewhere during the post "Remove transaction"
// gets called which will then wait for the global tx pool lock and deadlock.
go pool.eventMux.Post(TxPreEvent{tx})
// Try to insert the transaction into the pending queue
if pool.pending[addr] == nil {
pool.pending[addr] = newTxList(true)
}
list := pool.pending[addr]
inserted, old := list.Add(tx)
if !inserted {
// An older transaction was better, discard this
delete(pool.all, hash)
return
}
// Otherwise discard any previous transaction and mark this
if old != nil {
delete(pool.all, old.Hash())
}
pool.all[hash] = tx // Failsafe to work around direct pending inserts (tests)
// Set the potentially new pending nonce and notify any subsystems of the new tx
pool.beats[addr] = time.Now()
pool.pendingState.SetNonce(addr, tx.Nonce()+1)
go pool.eventMux.Post(TxPreEvent{tx})
}
// Add queues a single transaction in the pool if it is valid.
func (self *TxPool) Add(tx *types.Transaction) error {
self.mu.Lock()
defer self.mu.Unlock()
if err := self.add(tx); err != nil {
return err
}
self.checkQueue()
return nil
}
// AddTransactions attempts to queue all valid transactions in txs.
func (self *TxPool) AddTransactions(txs []*types.Transaction) {
self.mu.Lock()
defer self.mu.Unlock()
for _, tx := range txs {
if err := self.add(tx); err != nil {
glog.V(logger.Debug).Infoln("tx error:", err)
} else {
h := tx.Hash()
glog.V(logger.Debug).Infof("tx %x\n", h[:4])
}
}
// check and validate the queue
self.checkQueue()
}
// GetTransaction returns a transaction if it is contained in the pool
// and nil otherwise.
func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction {
tp.mu.RLock()
defer tp.mu.RUnlock()
// check the txs first
if tx, ok := tp.pending[hash]; ok {
return tx
}
// check queue
for _, txs := range tp.queue {
if tx, ok := txs[hash]; ok {
return tx
}
}
return nil
}
// GetTransactions returns all currently processable transactions.
// The returned slice may be modified by the caller.
func (self *TxPool) GetTransactions() (txs types.Transactions) {
self.mu.Lock()
defer self.mu.Unlock()
// check queue first
self.checkQueue()
// invalidate any txs
self.validatePool()
txs = make(types.Transactions, len(self.pending))
i := 0
for _, tx := range self.pending {
txs[i] = tx
i++
}
return txs
}
// GetQueuedTransactions returns all non-processable transactions.
func (self *TxPool) GetQueuedTransactions() types.Transactions {
self.mu.RLock()
defer self.mu.RUnlock()
var ret types.Transactions
for _, txs := range self.queue {
for _, tx := range txs {
ret = append(ret, tx)
}
}
sort.Sort(types.TxByNonce(ret))
return ret
}
// RemoveTransactions removes all given transactions from the pool.
func (self *TxPool) RemoveTransactions(txs types.Transactions) {
self.mu.Lock()
defer self.mu.Unlock()
for _, tx := range txs {
self.removeTx(tx.Hash())
}
}
// RemoveTx removes the transaction with the given hash from the pool.
func (pool *TxPool) RemoveTx(hash common.Hash) {
func (pool *TxPool) Add(tx *types.Transaction) error {
pool.mu.Lock()
defer pool.mu.Unlock()
if err := pool.add(tx); err != nil {
return err
}
pool.promoteExecutables()
return nil
}
// AddBatch attempts to queue a batch of transactions.
func (pool *TxPool) AddBatch(txs []*types.Transaction) {
pool.mu.Lock()
defer pool.mu.Unlock()
for _, tx := range txs {
if err := pool.add(tx); err != nil {
glog.V(logger.Debug).Infoln("tx error:", err)
}
}
pool.promoteExecutables()
}
// Get returns a transaction if it is contained in the pool
// and nil otherwise.
func (pool *TxPool) Get(hash common.Hash) *types.Transaction {
pool.mu.RLock()
defer pool.mu.RUnlock()
return pool.all[hash]
}
// Remove removes the transaction with the given hash from the pool.
func (pool *TxPool) Remove(hash common.Hash) {
pool.mu.Lock()
defer pool.mu.Unlock()
pool.removeTx(hash)
}
// RemoveBatch removes all given transactions from the pool.
func (pool *TxPool) RemoveBatch(txs types.Transactions) {
pool.mu.Lock()
defer pool.mu.Unlock()
for _, tx := range txs {
pool.removeTx(tx.Hash())
}
}
// removeTx removes a single transaction from the queue, moving all subsequent
// transactions back to the future queue.
func (pool *TxPool) removeTx(hash common.Hash) {
// delete from pending pool
delete(pool.pending, hash)
// delete from queue
for address, txs := range pool.queue {
if _, ok := txs[hash]; ok {
if len(txs) == 1 {
// if only one tx, remove entire address entry.
delete(pool.queue, address)
// Fetch the transaction we wish to delete
tx, ok := pool.all[hash]
if !ok {
return
}
addr, _ := tx.From() // already validated during insertion
// Remove it from the list of known transactions
delete(pool.all, hash)
// Remove the transaction from the pending lists and reset the account nonce
if pending := pool.pending[addr]; pending != nil {
if removed, invalids := pending.Remove(tx); removed {
// If no more transactions are left, remove the list
if pending.Empty() {
delete(pool.pending, addr)
delete(pool.beats, addr)
} else {
delete(txs, hash)
// Otherwise postpone any invalidated transactions
for _, tx := range invalids {
pool.enqueueTx(tx.Hash(), tx)
}
}
break
// Update the account nonce if needed
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
pool.pendingState.SetNonce(addr, tx.Nonce())
}
}
}
// Transaction is in the future queue
if future := pool.queue[addr]; future != nil {
future.Remove(tx)
if future.Empty() {
delete(pool.queue, addr)
}
}
}
// checkQueue moves transactions that have become processable to main pool.
func (pool *TxPool) checkQueue() {
// init delayed since tx pool could have been started before any state sync
// promoteExecutables moves transactions that have become processable from the
// future queue to the set of pending transactions. During this process, all
// invalidated transactions (low nonce, low balance) are deleted.
func (pool *TxPool) promoteExecutables() {
// Init delayed since tx pool could have been started before any state sync
if pool.pendingState == nil {
pool.resetState()
}
var promote txQueue
for address, txs := range pool.queue {
currentState, err := pool.currentState()
if err != nil {
glog.Errorf("could not get current state: %v", err)
return
// Retrieve the current state to allow nonce and balance checking
state, err := pool.currentState()
if err != nil {
glog.Errorf("Could not get current state: %v", err)
return
}
// Iterate over all accounts and promote any executable transactions
queued := uint64(0)
for addr, list := range pool.queue {
// Drop all transactions that are deemed too old (low nonce)
for _, tx := range list.Forward(state.GetNonce(addr)) {
if glog.V(logger.Core) {
glog.Infof("Removed old queued transaction: %v", tx)
}
delete(pool.all, tx.Hash())
}
balance := currentState.GetBalance(address)
// Drop all transactions that are too costly (low balance)
drops, _ := list.Filter(state.GetBalance(addr))
for _, tx := range drops {
if glog.V(logger.Core) {
glog.Infof("Removed unpayable queued transaction: %v", tx)
}
delete(pool.all, tx.Hash())
}
// Gather all executable transactions and promote them
for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {
if glog.V(logger.Core) {
glog.Infof("Promoting queued transaction: %v", tx)
}
pool.promoteTx(addr, tx.Hash(), tx)
}
// Drop all transactions over the allowed limit
for _, tx := range list.Cap(int(maxQueuedPerAccount)) {
if glog.V(logger.Core) {
glog.Infof("Removed cap-exceeding queued transaction: %v", tx)
}
delete(pool.all, tx.Hash())
}
queued += uint64(list.Len())
var (
guessedNonce = pool.pendingState.GetNonce(address) // nonce currently kept by the tx pool (pending state)
trueNonce = currentState.GetNonce(address) // nonce known by the last state
)
promote = promote[:0]
for hash, tx := range txs {
// Drop processed or out of fund transactions
if tx.Nonce() < trueNonce || balance.Cmp(tx.Cost()) < 0 {
if glog.V(logger.Core) {
glog.Infof("removed tx (%v) from pool queue: low tx nonce or out of funds\n", tx)
// Delete the entire queue entry if it became empty.
if list.Empty() {
delete(pool.queue, addr)
}
}
// If the pending limit is overflown, start equalizing allowances
pending := uint64(0)
for _, list := range pool.pending {
pending += uint64(list.Len())
}
if pending > maxPendingTotal {
// Assemble a spam order to penalize large transactors first
spammers := prque.New()
for addr, list := range pool.pending {
// Only evict transactions from high rollers
if uint64(list.Len()) > minPendingPerAccount {
// Skip local accounts as pools should maintain backlogs for themselves
for _, tx := range list.txs.items {
if !pool.localTx.contains(tx.Hash()) {
spammers.Push(addr, float32(list.Len()))
}
break // Checking on transaction for locality is enough
}
delete(txs, hash)
}
}
// Gradually drop transactions from offenders
offenders := []common.Address{}
for pending > maxPendingTotal && !spammers.Empty() {
// Retrieve the next offender if not local address
offender, _ := spammers.Pop()
offenders = append(offenders, offender.(common.Address))
// Equalize balances until all the same or below threshold
if len(offenders) > 1 {
// Calculate the equalization threshold for all current offenders
threshold := pool.pending[offender.(common.Address)].Len()
// Iteratively reduce all offenders until below limit or threshold reached
for pending > maxPendingTotal && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
for i := 0; i < len(offenders)-1; i++ {
list := pool.pending[offenders[i]]
list.Cap(list.Len() - 1)
pending--
}
}
}
}
// If still above threshold, reduce to limit or min allowance
if pending > maxPendingTotal && len(offenders) > 0 {
for pending > maxPendingTotal && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > minPendingPerAccount {
for _, addr := range offenders {
list := pool.pending[addr]
list.Cap(list.Len() - 1)
pending--
}
}
}
}
// If we've queued more transactions than the hard limit, drop oldest ones
if queued > maxQueuedInTotal {
// Sort all accounts with queued transactions by heartbeat
addresses := make(addresssByHeartbeat, 0, len(pool.queue))
for addr, _ := range pool.queue {
addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
}
sort.Sort(addresses)
// Drop transactions until the total is below the limit
for drop := queued - maxQueuedInTotal; drop > 0; {
addr := addresses[len(addresses)-1]
list := pool.queue[addr.address]
addresses = addresses[:len(addresses)-1]
// Drop all transactions if they are less than the overflow
if size := uint64(list.Len()); size <= drop {
for _, tx := range list.Flatten() {
pool.removeTx(tx.Hash())
}
drop -= size
continue
}
// Collect the remaining transactions for the next pass.
promote = append(promote, txQueueEntry{hash, address, tx})
}
// Find the next consecutive nonce range starting at the current account nonce,
// pushing the guessed nonce forward if we add consecutive transactions.
sort.Sort(promote)
for i, entry := range promote {
// If we reached a gap in the nonces, enforce transaction limit and stop
if entry.Nonce() > guessedNonce {
if len(promote)-i > maxQueued {
if glog.V(logger.Debug) {
glog.Infof("Queued tx limit exceeded for %s. Tx %s removed\n", common.PP(address[:]), common.PP(entry.hash[:]))
}
for _, drop := range promote[i+maxQueued:] {
delete(txs, drop.hash)
}
}
break
// Otherwise drop only last few transactions
txs := list.Flatten()
for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
pool.removeTx(txs[i].Hash())
drop--
}
// Otherwise promote the transaction and move the guess nonce if needed
pool.addTx(entry.hash, address, entry.Transaction)
delete(txs, entry.hash)
if entry.Nonce() == guessedNonce {
guessedNonce++
}
}
// Delete the entire queue entry if it became empty.
if len(txs) == 0 {
delete(pool.queue, address)
}
}
}
// validatePool removes invalid and processed transactions from the main pool.
// If a transaction is removed for being invalid (e.g. out of funds), all sub-
// sequent (Still valid) transactions are moved back into the future queue. This
// is important to prevent a drained account from DOSing the network with non
// executable transactions.
func (pool *TxPool) validatePool() {
// demoteUnexecutables removes invalid and processed transactions from the pools
// executable/pending queue and any subsequent transactions that become unexecutable
// are moved back into the future queue.
func (pool *TxPool) demoteUnexecutables() {
// Retrieve the current state to allow nonce and balance checking
state, err := pool.currentState()
if err != nil {
glog.V(logger.Info).Infoln("failed to get current state: %v", err)
return
}
balanceCache := make(map[common.Address]*big.Int)
// Iterate over all accounts and demote any non-executable transactions
for addr, list := range pool.pending {
nonce := state.GetNonce(addr)
// Clean up the pending pool, accumulating invalid nonces
gaps := make(map[common.Address]uint64)
for hash, tx := range pool.pending {
sender, _ := tx.From() // err already checked
// Perform light nonce and balance validation
balance := balanceCache[sender]
if balance == nil {
balance = state.GetBalance(sender)
balanceCache[sender] = balance
}
if past := state.GetNonce(sender) > tx.Nonce(); past || balance.Cmp(tx.Cost()) < 0 {
// Remove an already past it invalidated transaction
// Drop all transactions that are deemed too old (low nonce)
for _, tx := range list.Forward(nonce) {
if glog.V(logger.Core) {
glog.Infof("removed tx (%v) from pool: low tx nonce or out of funds\n", tx)
}
delete(pool.pending, hash)
// Track the smallest invalid nonce to postpone subsequent transactions
if !past {
if prev, ok := gaps[sender]; !ok || tx.Nonce() < prev {
gaps[sender] = tx.Nonce()
}
glog.Infof("Removed old pending transaction: %v", tx)
}
delete(pool.all, tx.Hash())
}
}
// Move all transactions after a gap back to the future queue
if len(gaps) > 0 {
for hash, tx := range pool.pending {
sender, _ := tx.From()
if gap, ok := gaps[sender]; ok && tx.Nonce() >= gap {
if glog.V(logger.Core) {
glog.Infof("postponed tx (%v) due to introduced gap\n", tx)
}
pool.queueTx(hash, tx)
delete(pool.pending, hash)
// Drop all transactions that are too costly (low balance), and queue any invalids back for later
drops, invalids := list.Filter(state.GetBalance(addr))
for _, tx := range drops {
if glog.V(logger.Core) {
glog.Infof("Removed unpayable pending transaction: %v", tx)
}
delete(pool.all, tx.Hash())
}
for _, tx := range invalids {
if glog.V(logger.Core) {
glog.Infof("Demoting pending transaction: %v", tx)
}
pool.enqueueTx(tx.Hash(), tx)
}
// Delete the entire queue entry if it became empty.
if list.Empty() {
delete(pool.pending, addr)
delete(pool.beats, addr)
}
}
}
type txQueue []txQueueEntry
// expirationLoop is a loop that periodically iterates over all accounts with
// queued transactions and drop all that have been inactive for a prolonged amount
// of time.
func (pool *TxPool) expirationLoop() {
defer pool.wg.Done()
type txQueueEntry struct {
hash common.Hash
addr common.Address
*types.Transaction
evict := time.NewTicker(evictionInterval)
defer evict.Stop()
for {
select {
case <-evict.C:
pool.mu.Lock()
for addr := range pool.queue {
if time.Since(pool.beats[addr]) > maxQueuedLifetime {
for _, tx := range pool.queue[addr].Flatten() {
pool.removeTx(tx.Hash())
}
}
}
pool.mu.Unlock()
case <-pool.quit:
return
}
}
}
func (q txQueue) Len() int { return len(q) }
func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
func (q txQueue) Less(i, j int) bool { return q[i].Nonce() < q[j].Nonce() }
// addressByHeartbeat is an account address tagged with its last activity timestamp.
type addressByHeartbeat struct {
address common.Address
heartbeat time.Time
}
type addresssByHeartbeat []addressByHeartbeat
func (a addresssByHeartbeat) Len() int { return len(a) }
func (a addresssByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) }
func (a addresssByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// txSet represents a set of transaction hashes in which entries
// are automatically dropped after txSetDuration time

View File

@@ -19,7 +19,9 @@ package core
import (
"crypto/ecdsa"
"math/big"
"math/rand"
"testing"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
@@ -38,10 +40,10 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
var m event.TypeMux
key, _ := crypto.GenerateKey()
newPool := NewTxPool(testChainConfig(), &m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
newPool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
newPool.resetState()
return newPool, key
}
@@ -91,9 +93,9 @@ func TestTransactionQueue(t *testing.T) {
from, _ := tx.From()
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1000))
pool.queueTx(tx.Hash(), tx)
pool.enqueueTx(tx.Hash(), tx)
pool.checkQueue()
pool.promoteExecutables()
if len(pool.pending) != 1 {
t.Error("expected valid txs to be 1 is", len(pool.pending))
}
@@ -101,14 +103,14 @@ func TestTransactionQueue(t *testing.T) {
tx = transaction(1, big.NewInt(100), key)
from, _ = tx.From()
currentState.SetNonce(from, 2)
pool.queueTx(tx.Hash(), tx)
pool.checkQueue()
if _, ok := pool.pending[tx.Hash()]; ok {
pool.enqueueTx(tx.Hash(), tx)
pool.promoteExecutables()
if _, ok := pool.pending[from].txs.items[tx.Nonce()]; ok {
t.Error("expected transaction to be in tx pool")
}
if len(pool.queue[from]) > 0 {
t.Error("expected transaction queue to be empty. is", len(pool.queue[from]))
if len(pool.queue) > 0 {
t.Error("expected transaction queue to be empty. is", len(pool.queue))
}
pool, key = setupTxPool()
@@ -118,17 +120,17 @@ func TestTransactionQueue(t *testing.T) {
from, _ = tx1.From()
currentState, _ = pool.currentState()
currentState.AddBalance(from, big.NewInt(1000))
pool.queueTx(tx1.Hash(), tx1)
pool.queueTx(tx2.Hash(), tx2)
pool.queueTx(tx3.Hash(), tx3)
pool.enqueueTx(tx1.Hash(), tx1)
pool.enqueueTx(tx2.Hash(), tx2)
pool.enqueueTx(tx3.Hash(), tx3)
pool.checkQueue()
pool.promoteExecutables()
if len(pool.pending) != 1 {
t.Error("expected tx pool to be 1, got", len(pool.pending))
}
if len(pool.queue[from]) != 2 {
t.Error("expected len(queue) == 2, got", len(pool.queue[from]))
if pool.queue[from].Len() != 2 {
t.Error("expected len(queue) == 2, got", pool.queue[from].Len())
}
}
@@ -138,24 +140,21 @@ func TestRemoveTx(t *testing.T) {
from, _ := tx.From()
currentState, _ := pool.currentState()
currentState.AddBalance(from, big.NewInt(1))
pool.queueTx(tx.Hash(), tx)
pool.addTx(tx.Hash(), from, tx)
pool.enqueueTx(tx.Hash(), tx)
pool.promoteTx(from, tx.Hash(), tx)
if len(pool.queue) != 1 {
t.Error("expected queue to be 1, got", len(pool.queue))
}
if len(pool.pending) != 1 {
t.Error("expected txs to be 1, got", len(pool.pending))
t.Error("expected pending to be 1, got", len(pool.pending))
}
pool.RemoveTx(tx.Hash())
pool.Remove(tx.Hash())
if len(pool.queue) > 0 {
t.Error("expected queue to be 0, got", len(pool.queue))
}
if len(pool.pending) > 0 {
t.Error("expected txs to be 0, got", len(pool.pending))
t.Error("expected pending to be 0, got", len(pool.pending))
}
}
@@ -188,7 +187,7 @@ func TestTransactionChainFork(t *testing.T) {
if err := pool.add(tx); err != nil {
t.Error("didn't expect error", err)
}
pool.RemoveTransactions([]*types.Transaction{tx})
pool.RemoveBatch([]*types.Transaction{tx})
// reset the pool's internal state
resetState()
@@ -210,18 +209,38 @@ func TestTransactionDoubleNonce(t *testing.T) {
}
resetState()
tx := transaction(0, big.NewInt(100000), key)
tx2 := transaction(0, big.NewInt(1000000), key)
if err := pool.add(tx); err != nil {
tx1, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(100000), big.NewInt(1), nil).SignECDSA(key)
tx2, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(2), nil).SignECDSA(key)
tx3, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(1), nil).SignECDSA(key)
// Add the first two transaction, ensure higher priced stays only
if err := pool.add(tx1); err != nil {
t.Error("didn't expect error", err)
}
if err := pool.add(tx2); err != nil {
t.Error("didn't expect error", err)
}
pool.checkQueue()
if len(pool.pending) != 2 {
t.Error("expected 2 pending txs. Got", len(pool.pending))
pool.promoteExecutables()
if pool.pending[addr].Len() != 1 {
t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
}
if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() {
t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash())
}
// Add the thid transaction and ensure it's not saved (smaller price)
if err := pool.add(tx3); err != nil {
t.Error("didn't expect error", err)
}
pool.promoteExecutables()
if pool.pending[addr].Len() != 1 {
t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
}
if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() {
t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash())
}
// Ensure the total transaction count is correct
if len(pool.all) != 1 {
t.Error("expected 1 total transactions, got", len(pool.all))
}
}
@@ -237,8 +256,11 @@ func TestMissingNonce(t *testing.T) {
if len(pool.pending) != 0 {
t.Error("expected 0 pending transactions, got", len(pool.pending))
}
if len(pool.queue[addr]) != 1 {
t.Error("expected 1 queued transaction, got", len(pool.queue[addr]))
if pool.queue[addr].Len() != 1 {
t.Error("expected 1 queued transaction, got", pool.queue[addr].Len())
}
if len(pool.all) != 1 {
t.Error("expected 1 total transactions, got", len(pool.all))
}
}
@@ -270,8 +292,11 @@ func TestRemovedTxEvent(t *testing.T) {
currentState.AddBalance(from, big.NewInt(1000000000000))
pool.eventMux.Post(RemovedTransactionEvent{types.Transactions{tx}})
pool.eventMux.Post(ChainHeadEvent{nil})
if len(pool.pending) != 1 {
t.Error("expected 1 pending tx, got", len(pool.pending))
if pool.pending[from].Len() != 1 {
t.Error("expected 1 pending tx, got", pool.pending[from].Len())
}
if len(pool.all) != 1 {
t.Error("expected 1 total transactions, got", len(pool.all))
}
}
@@ -292,41 +317,50 @@ func TestTransactionDropping(t *testing.T) {
tx10 = transaction(10, big.NewInt(100), key)
tx11 = transaction(11, big.NewInt(200), key)
)
pool.addTx(tx0.Hash(), account, tx0)
pool.addTx(tx1.Hash(), account, tx1)
pool.queueTx(tx10.Hash(), tx10)
pool.queueTx(tx11.Hash(), tx11)
pool.promoteTx(account, tx0.Hash(), tx0)
pool.promoteTx(account, tx1.Hash(), tx1)
pool.enqueueTx(tx10.Hash(), tx10)
pool.enqueueTx(tx11.Hash(), tx11)
// Check that pre and post validations leave the pool as is
if len(pool.pending) != 2 {
t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), 2)
if pool.pending[account].Len() != 2 {
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2)
}
if len(pool.queue[account]) != 2 {
t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 2)
if pool.queue[account].Len() != 2 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2)
}
if len(pool.all) != 4 {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
}
pool.resetState()
if len(pool.pending) != 2 {
t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), 2)
if pool.pending[account].Len() != 2 {
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2)
}
if len(pool.queue[account]) != 2 {
t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 2)
if pool.queue[account].Len() != 2 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2)
}
if len(pool.all) != 4 {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
}
// Reduce the balance of the account, and check that invalidated transactions are dropped
state.AddBalance(account, big.NewInt(-750))
pool.resetState()
if _, ok := pool.pending[tx0.Hash()]; !ok {
if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
t.Errorf("funded pending transaction missing: %v", tx0)
}
if _, ok := pool.pending[tx1.Hash()]; ok {
if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; ok {
t.Errorf("out-of-fund pending transaction present: %v", tx1)
}
if _, ok := pool.queue[account][tx10.Hash()]; !ok {
if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok {
t.Errorf("funded queued transaction missing: %v", tx10)
}
if _, ok := pool.queue[account][tx11.Hash()]; ok {
if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
t.Errorf("out-of-fund queued transaction present: %v", tx11)
}
if len(pool.all) != 2 {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 2)
}
}
// Tests that if a transaction is dropped from the current pending pool (e.g. out
@@ -349,55 +383,64 @@ func TestTransactionPostponing(t *testing.T) {
} else {
tx = transaction(uint64(i), big.NewInt(500), key)
}
pool.addTx(tx.Hash(), account, tx)
pool.promoteTx(account, tx.Hash(), tx)
txns = append(txns, tx)
}
// Check that pre and post validations leave the pool as is
if len(pool.pending) != len(txns) {
t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), len(txns))
if pool.pending[account].Len() != len(txns) {
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns))
}
if len(pool.queue[account]) != 0 {
t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 0)
if len(pool.queue) != 0 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0)
}
if len(pool.all) != len(txns) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns))
}
pool.resetState()
if len(pool.pending) != len(txns) {
t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), len(txns))
if pool.pending[account].Len() != len(txns) {
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns))
}
if len(pool.queue[account]) != 0 {
t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 0)
if len(pool.queue) != 0 {
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0)
}
if len(pool.all) != len(txns) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns))
}
// Reduce the balance of the account, and check that transactions are reorganised
state.AddBalance(account, big.NewInt(-750))
pool.resetState()
if _, ok := pool.pending[txns[0].Hash()]; !ok {
if _, ok := pool.pending[account].txs.items[txns[0].Nonce()]; !ok {
t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txns[0])
}
if _, ok := pool.queue[account][txns[0].Hash()]; ok {
if _, ok := pool.queue[account].txs.items[txns[0].Nonce()]; ok {
t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txns[0])
}
for i, tx := range txns[1:] {
if i%2 == 1 {
if _, ok := pool.pending[tx.Hash()]; ok {
if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: valid but future transaction present in pending pool: %v", i+1, tx)
}
if _, ok := pool.queue[account][tx.Hash()]; !ok {
if _, ok := pool.queue[account].txs.items[tx.Nonce()]; !ok {
t.Errorf("tx %d: valid but future transaction missing from future queue: %v", i+1, tx)
}
} else {
if _, ok := pool.pending[tx.Hash()]; ok {
if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx)
}
if _, ok := pool.queue[account][tx.Hash()]; ok {
if _, ok := pool.queue[account].txs.items[tx.Nonce()]; ok {
t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx)
}
}
}
if len(pool.all) != len(txns)/2 {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)/2)
}
}
// Tests that if the transaction count belonging to a single account goes above
// some threshold, the higher transactions are dropped to prevent DOS attacks.
func TestTransactionQueueLimiting(t *testing.T) {
func TestTransactionQueueAccountLimiting(t *testing.T) {
// Create a test account and fund it
pool, key := setupTxPool()
account, _ := transaction(0, big.NewInt(0), key).From()
@@ -406,23 +449,104 @@ func TestTransactionQueueLimiting(t *testing.T) {
state.AddBalance(account, big.NewInt(1000000))
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(1); i <= maxQueued+5; i++ {
for i := uint64(1); i <= maxQueuedPerAccount+5; i++ {
if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
if len(pool.pending) != 0 {
t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), 0)
}
if i <= maxQueued {
if len(pool.queue[account]) != int(i) {
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, len(pool.queue[account]), i)
if i <= maxQueuedPerAccount {
if pool.queue[account].Len() != int(i) {
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), i)
}
} else {
if len(pool.queue[account]) != maxQueued {
t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, len(pool.queue[account]), maxQueued)
if pool.queue[account].Len() != int(maxQueuedPerAccount) {
t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), maxQueuedPerAccount)
}
}
}
if len(pool.all) != int(maxQueuedPerAccount) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), maxQueuedPerAccount)
}
}
// Tests that if the transaction count belonging to multiple accounts go above
// some threshold, the higher transactions are dropped to prevent DOS attacks.
func TestTransactionQueueGlobalLimiting(t *testing.T) {
// Reduce the queue limits to shorten test time
defer func(old uint64) { maxQueuedInTotal = old }(maxQueuedInTotal)
maxQueuedInTotal = maxQueuedPerAccount * 3
// Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState()
// Create a number of test accounts and fund them
state, _ := pool.currentState()
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Generate and queue a batch of transactions
nonces := make(map[common.Address]uint64)
txs := make(types.Transactions, 0, 3*maxQueuedInTotal)
for len(txs) < cap(txs) {
key := keys[rand.Intn(len(keys))]
addr := crypto.PubkeyToAddress(key.PublicKey)
txs = append(txs, transaction(nonces[addr]+1, big.NewInt(100000), key))
nonces[addr]++
}
// Import the batch and verify that limits have been enforced
pool.AddBatch(txs)
queued := 0
for addr, list := range pool.queue {
if list.Len() > int(maxQueuedPerAccount) {
t.Errorf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), maxQueuedPerAccount)
}
queued += list.Len()
}
if queued > int(maxQueuedInTotal) {
t.Fatalf("total transactions overflow allowance: %d > %d", queued, maxQueuedInTotal)
}
}
// Tests that if an account remains idle for a prolonged amount of time, any
// non-executable transactions queued up are dropped to prevent wasting resources
// on shuffling them around.
func TestTransactionQueueTimeLimiting(t *testing.T) {
// Reduce the queue limits to shorten test time
defer func(old time.Duration) { maxQueuedLifetime = old }(maxQueuedLifetime)
defer func(old time.Duration) { evictionInterval = old }(evictionInterval)
maxQueuedLifetime = time.Second
evictionInterval = time.Second
// Create a test account and fund it
pool, key := setupTxPool()
account, _ := transaction(0, big.NewInt(0), key).From()
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
// Queue up a batch of transactions
for i := uint64(1); i <= maxQueuedPerAccount; i++ {
if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
}
// Wait until at least two expiration cycles hit and make sure the transactions are gone
time.Sleep(2 * evictionInterval)
if len(pool.queue) > 0 {
t.Fatalf("old transactions remained after eviction")
}
}
// Tests that even if the transaction count belonging to a single account goes
@@ -437,17 +561,20 @@ func TestTransactionPendingLimiting(t *testing.T) {
state.AddBalance(account, big.NewInt(1000000))
// Keep queuing up transactions and make sure all above a limit are dropped
for i := uint64(0); i < maxQueued+5; i++ {
for i := uint64(0); i < maxQueuedPerAccount+5; i++ {
if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
if len(pool.pending) != int(i)+1 {
t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), i+1)
if pool.pending[account].Len() != int(i)+1 {
t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, pool.pending[account].Len(), i+1)
}
if len(pool.queue[account]) != 0 {
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, len(pool.queue[account]), 0)
if len(pool.queue) != 0 {
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0)
}
}
if len(pool.all) != int(maxQueuedPerAccount+5) {
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), maxQueuedPerAccount+5)
}
}
// Tests that the transaction limits are enforced the same way irrelevant whether
@@ -462,39 +589,132 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
state1, _ := pool1.currentState()
state1.AddBalance(account1, big.NewInt(1000000))
for i := uint64(0); i < maxQueued+5; i++ {
for i := uint64(0); i < maxQueuedPerAccount+5; i++ {
if err := pool1.Add(transaction(origin+i, big.NewInt(100000), key1)); err != nil {
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
}
}
// Add a batch of transactions to a pool in one bit batch
// Add a batch of transactions to a pool in one big batch
pool2, key2 := setupTxPool()
account2, _ := transaction(0, big.NewInt(0), key2).From()
state2, _ := pool2.currentState()
state2.AddBalance(account2, big.NewInt(1000000))
txns := []*types.Transaction{}
for i := uint64(0); i < maxQueued+5; i++ {
for i := uint64(0); i < maxQueuedPerAccount+5; i++ {
txns = append(txns, transaction(origin+i, big.NewInt(100000), key2))
}
pool2.AddTransactions(txns)
pool2.AddBatch(txns)
// Ensure the batch optimization honors the same pool mechanics
if len(pool1.pending) != len(pool2.pending) {
t.Errorf("pending transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.pending), len(pool2.pending))
}
if len(pool1.queue[account1]) != len(pool2.queue[account2]) {
t.Errorf("queued transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.queue[account1]), len(pool2.queue[account2]))
if len(pool1.queue) != len(pool2.queue) {
t.Errorf("queued transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.queue), len(pool2.queue))
}
if len(pool1.all) != len(pool2.all) {
t.Errorf("total transaction count mismatch: one-by-one algo %d, batch algo %d", len(pool1.all), len(pool2.all))
}
}
// Tests that if the transaction count belonging to multiple accounts go above
// some hard threshold, the higher transactions are dropped to prevent DOS
// attacks.
func TestTransactionPendingGlobalLimiting(t *testing.T) {
// Reduce the queue limits to shorten test time
defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal)
maxPendingTotal = minPendingPerAccount * 10
// Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState()
// Create a number of test accounts and fund them
state, _ := pool.currentState()
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Generate and queue a batch of transactions
nonces := make(map[common.Address]uint64)
txs := types.Transactions{}
for _, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
for j := 0; j < int(maxPendingTotal)/len(keys)*2; j++ {
txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
nonces[addr]++
}
}
// Import the batch and verify that limits have been enforced
pool.AddBatch(txs)
pending := 0
for _, list := range pool.pending {
pending += list.Len()
}
if pending > int(maxPendingTotal) {
t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, maxPendingTotal)
}
}
// Tests that if the transaction count belonging to multiple accounts go above
// some hard threshold, if they are under the minimum guaranteed slot count then
// the transactions are still kept.
func TestTransactionPendingMinimumAllowance(t *testing.T) {
// Reduce the queue limits to shorten test time
defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal)
maxPendingTotal = 0
// Create the pool to test the limit enforcement with
db, _ := ethdb.NewMemDatabase()
statedb, _ := state.New(common.Hash{}, db)
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
pool.resetState()
// Create a number of test accounts and fund them
state, _ := pool.currentState()
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
keys[i], _ = crypto.GenerateKey()
state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
}
// Generate and queue a batch of transactions
nonces := make(map[common.Address]uint64)
txs := types.Transactions{}
for _, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
for j := 0; j < int(minPendingPerAccount)*2; j++ {
txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
nonces[addr]++
}
}
// Import the batch and verify that limits have been enforced
pool.AddBatch(txs)
for addr, list := range pool.pending {
if list.Len() != int(minPendingPerAccount) {
t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), minPendingPerAccount)
}
}
}
// Benchmarks the speed of validating the contents of the pending queue of the
// transaction pool.
func BenchmarkValidatePool100(b *testing.B) { benchmarkValidatePool(b, 100) }
func BenchmarkValidatePool1000(b *testing.B) { benchmarkValidatePool(b, 1000) }
func BenchmarkValidatePool10000(b *testing.B) { benchmarkValidatePool(b, 10000) }
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
func BenchmarkPendingDemotion1000(b *testing.B) { benchmarkPendingDemotion(b, 1000) }
func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 10000) }
func benchmarkValidatePool(b *testing.B, size int) {
func benchmarkPendingDemotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
pool, key := setupTxPool()
account, _ := transaction(0, big.NewInt(0), key).From()
@@ -503,22 +723,22 @@ func benchmarkValidatePool(b *testing.B, size int) {
for i := 0; i < size; i++ {
tx := transaction(uint64(i), big.NewInt(100000), key)
pool.addTx(tx.Hash(), account, tx)
pool.promoteTx(account, tx.Hash(), tx)
}
// Benchmark the speed of pool validation
b.ResetTimer()
for i := 0; i < b.N; i++ {
pool.validatePool()
pool.demoteUnexecutables()
}
}
// Benchmarks the speed of scheduling the contents of the future queue of the
// transaction pool.
func BenchmarkCheckQueue100(b *testing.B) { benchmarkCheckQueue(b, 100) }
func BenchmarkCheckQueue1000(b *testing.B) { benchmarkCheckQueue(b, 1000) }
func BenchmarkCheckQueue10000(b *testing.B) { benchmarkCheckQueue(b, 10000) }
func BenchmarkFuturePromotion100(b *testing.B) { benchmarkFuturePromotion(b, 100) }
func BenchmarkFuturePromotion1000(b *testing.B) { benchmarkFuturePromotion(b, 1000) }
func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 10000) }
func benchmarkCheckQueue(b *testing.B, size int) {
func benchmarkFuturePromotion(b *testing.B, size int) {
// Add a batch of transactions to a pool one by one
pool, key := setupTxPool()
account, _ := transaction(0, big.NewInt(0), key).From()
@@ -527,11 +747,56 @@ func benchmarkCheckQueue(b *testing.B, size int) {
for i := 0; i < size; i++ {
tx := transaction(uint64(1+i), big.NewInt(100000), key)
pool.queueTx(tx.Hash(), tx)
pool.enqueueTx(tx.Hash(), tx)
}
// Benchmark the speed of pool validation
b.ResetTimer()
for i := 0; i < b.N; i++ {
pool.checkQueue()
pool.promoteExecutables()
}
}
// Benchmarks the speed of iterative transaction insertion.
func BenchmarkPoolInsert(b *testing.B) {
// Generate a batch of transactions to enqueue into the pool
pool, key := setupTxPool()
account, _ := transaction(0, big.NewInt(0), key).From()
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
txs := make(types.Transactions, b.N)
for i := 0; i < b.N; i++ {
txs[i] = transaction(uint64(i), big.NewInt(100000), key)
}
// Benchmark importing the transactions into the queue
b.ResetTimer()
for _, tx := range txs {
pool.Add(tx)
}
}
// 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 benchmarkPoolBatchInsert(b *testing.B, size int) {
// Generate a batch of transactions to enqueue into the pool
pool, key := setupTxPool()
account, _ := transaction(0, big.NewInt(0), key).From()
state, _ := pool.currentState()
state.AddBalance(account, big.NewInt(1000000))
batches := make([]types.Transactions, b.N)
for i := 0; i < b.N; i++ {
batches[i] = make(types.Transactions, size)
for j := 0; j < size; j++ {
batches[i][j] = transaction(uint64(size*i+j), big.NewInt(100000), key)
}
}
// Benchmark importing the transactions into the queue
b.ResetTimer()
for _, batch := range batches {
pool.AddBatch(batch)
}
}

View File

@@ -23,19 +23,18 @@ import (
"fmt"
"io"
"math/big"
"sort"
"sync/atomic"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/rlp"
)
var ErrInvalidSig = errors.New("invalid v, r, s values")
type Transaction struct {
signer Signer
data txdata
// caches
hash atomic.Value
@@ -49,24 +48,27 @@ type txdata struct {
Recipient *common.Address `rlp:"nil"` // nil means contract creation
Amount *big.Int
Payload []byte
V byte // signature
R, S *big.Int // signature
V, R, S *big.Int // signature
}
func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
if len(data) > 0 {
data = common.CopyBytes(data)
}
return &Transaction{data: txdata{
AccountNonce: nonce,
Recipient: nil,
Amount: new(big.Int).Set(amount),
GasLimit: new(big.Int).Set(gasLimit),
Price: new(big.Int).Set(gasPrice),
Payload: data,
R: new(big.Int),
S: new(big.Int),
}}
return &Transaction{
signer: HomesteadSigner{},
data: txdata{
AccountNonce: nonce,
Recipient: nil,
Amount: new(big.Int).Set(amount),
GasLimit: new(big.Int).Set(gasLimit),
Price: new(big.Int).Set(gasPrice),
Payload: data,
V: new(big.Int),
R: new(big.Int),
S: new(big.Int),
},
}
}
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
@@ -80,6 +82,7 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice
Amount: new(big.Int),
GasLimit: new(big.Int),
Price: new(big.Int),
V: new(big.Int),
R: new(big.Int),
S: new(big.Int),
}
@@ -92,7 +95,24 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice
if gasPrice != nil {
d.Price.Set(gasPrice)
}
return &Transaction{data: d}
return &Transaction{signer: HomesteadSigner{}, data: d}
}
func (tx *Transaction) SetSigner(s Signer) {
// reset the cached value incase another value was cached
tx.from.Store((*common.Address)(nil))
tx.signer = s
}
// ChainId returns which chain id this transaction was signed for (if at all)
func (tx *Transaction) ChainId() *big.Int {
return deriveChainId(tx.data.V)
}
// Protected returns whether the transaction is pretected from replay protection
func (tx *Transaction) Protected() bool {
return isProtectedV(tx.data.V)
}
func (tx *Transaction) EncodeRLP(w io.Writer) error {
@@ -105,6 +125,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
if err == nil {
tx.size.Store(common.StorageSize(rlp.ListSize(size)))
}
tx.signer = HomesteadSigner{}
return err
}
@@ -137,14 +158,7 @@ func (tx *Transaction) Hash() common.Hash {
// SigHash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (tx *Transaction) SigHash() common.Hash {
return rlpHash([]interface{}{
tx.data.AccountNonce,
tx.data.Price,
tx.data.GasLimit,
tx.data.Recipient,
tx.data.Amount,
tx.data.Payload,
})
return tx.signer.Hash(tx)
}
func (tx *Transaction) Size() common.StorageSize {
@@ -195,15 +209,17 @@ func (tx *Transaction) FromFrontier() (common.Address, error) {
func doFrom(tx *Transaction, homestead bool) (common.Address, error) {
if from := tx.from.Load(); from != nil {
return from.(common.Address), nil
if faddr := from.(*common.Address); faddr != nil {
return *faddr, nil
}
}
pubkey, err := tx.publicKey(homestead)
pubkey, err := tx.signer.PublicKey(tx)
if err != nil {
return common.Address{}, err
}
var addr common.Address
copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
tx.from.Store(addr)
tx.from.Store(&addr)
return addr, nil
}
@@ -215,52 +231,20 @@ func (tx *Transaction) Cost() *big.Int {
}
func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) {
return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
return SignatureValues(tx.signer, tx)
}
func (tx *Transaction) publicKey(homestead bool) ([]byte, error) {
if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S, homestead) {
return nil, ErrInvalidSig
}
// encode the signature in uncompressed format
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
sig := make([]byte, 65)
copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
sig[64] = tx.data.V - 27
// recover the public key from the signature
hash := tx.SigHash()
pub, err := crypto.Ecrecover(hash[:], sig)
if err != nil {
glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err)
return nil, err
}
if len(pub) == 0 || pub[0] != 4 {
return nil, errors.New("invalid public key")
}
return pub, nil
func (tx *Transaction) RawSignatureValues() (v *big.Int, r *big.Int, s *big.Int) {
return tx.data.V, tx.data.R, tx.data.S
}
func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) {
if len(sig) != 65 {
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
}
cpy := &Transaction{data: tx.data}
cpy.data.R = new(big.Int).SetBytes(sig[:32])
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
cpy.data.V = sig[64] + 27
return cpy, nil
return tx.signer.WithSignature(tx, sig)
}
func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) {
h := tx.SigHash()
sig, err := crypto.Sign(h[:], prv)
if err != nil {
return nil, err
}
return tx.WithSignature(sig)
tx, err := tx.signer.SignECDSA(tx, prv)
return tx, err
}
func (tx *Transaction) String() string {
@@ -369,49 +353,58 @@ func (s *TxByPrice) Pop() interface{} {
return x
}
// SortByPriceAndNonce sorts the transactions by price in such a way that the
// nonce orderings within a single account are maintained.
// TransactionsByPriceAndNonce represents a set of transactions that can return
// transactions in a profit-maximising sorted order, while supporting removing
// 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)
}
// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve
// price sorted transactions in a nonce-honouring way.
//
// Note, this is not as trivial as it seems from the first look as there are three
// different criteria that need to be taken into account (price, nonce, account
// match), which cannot be done with any plain sorting method, as certain items
// cannot be compared without context.
//
// This method first sorts the separates the list of transactions into individual
// sender accounts and sorts them by nonce. After the account nonce ordering is
// satisfied, the results are merged back together by price, always comparing only
// the head transaction from each account. This is done via a heap to keep it fast.
func SortByPriceAndNonce(txs []*Transaction) {
// Separate the transactions by account and sort by nonce
byNonce := make(map[common.Address][]*Transaction)
for _, tx := range txs {
acc, _ := tx.From() // we only sort valid txs so this cannot fail
byNonce[acc] = append(byNonce[acc], tx)
}
for _, accTxs := range byNonce {
sort.Sort(TxByNonce(accTxs))
}
// Note, the input map is reowned so the caller should not interact any more with
// if after providng it to the constructor.
func NewTransactionsByPriceAndNonce(txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
// Initialize a price based heap with the head transactions
byPrice := make(TxByPrice, 0, len(byNonce))
for acc, accTxs := range byNonce {
byPrice = append(byPrice, accTxs[0])
byNonce[acc] = accTxs[1:]
heads := make(TxByPrice, 0, len(txs))
for acc, accTxs := range txs {
heads = append(heads, accTxs[0])
txs[acc] = accTxs[1:]
}
heap.Init(&byPrice)
heap.Init(&heads)
// Merge by replacing the best with the next from the same account
txs = txs[:0]
for len(byPrice) > 0 {
// Retrieve the next best transaction by price
best := heap.Pop(&byPrice).(*Transaction)
// Push in its place the next transaction from the same account
acc, _ := best.From() // we only sort valid txs so this cannot fail
if accTxs, ok := byNonce[acc]; ok && len(accTxs) > 0 {
heap.Push(&byPrice, accTxs[0])
byNonce[acc] = accTxs[1:]
}
// Accumulate the best priced transaction
txs = append(txs, best)
// Assemble and return the transaction set
return &TransactionsByPriceAndNonce{
txs: txs,
heads: heads,
}
}
// Peek returns the next transaction by price.
func (t *TransactionsByPriceAndNonce) Peek() *Transaction {
if len(t.heads) == 0 {
return nil
}
return t.heads[0]
}
// Shift replaces the current best head with the next one from the same account.
func (t *TransactionsByPriceAndNonce) Shift() {
acc, _ := t.heads[0].From() // we only sort valid txs so this cannot fail
if txs, ok := t.txs[acc]; ok && len(txs) > 0 {
t.heads[0], t.txs[acc] = txs[0], txs[1:]
heap.Fix(&t.heads, 0)
} else {
heap.Pop(&t.heads)
}
}
// Pop removes the best transaction, *not* replacing it with the next one from
// the same account. This should be used when a transaction cannot be executed
// and hence all subsequent ones should be discarded from the same account.
func (t *TransactionsByPriceAndNonce) Pop() {
heap.Pop(&t.heads)
}

View File

@@ -0,0 +1,376 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package types
import (
"crypto/ecdsa"
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
var ErrInvalidChainId = errors.New("invalid chain id for signer")
// deriveSigner makes a *best* guess about which signer to use.
func deriveSigner(V *big.Int) Signer {
if V.BitLen() > 0 && isProtectedV(V) {
return EIP155Signer{chainId: deriveChainId(V)}
} else {
return HomesteadSigner{}
}
}
func pickSigner(rules params.Rules) Signer {
var signer Signer
switch {
case rules.IsEIP155:
signer = NewEIP155Signer(rules.ChainId)
case rules.IsHomestead:
signer = HomesteadSigner{}
default:
signer = FrontierSigner{}
}
return signer
}
func isProtectedV(V *big.Int) bool {
if V.BitLen() <= 8 {
v := V.Uint64()
return v != 27 && v != 28
}
// anything not 27 or 28 are considered unprotected
return true
}
// MakeSigner returns a Signer based on the given chain config and block number.
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
var signer Signer
switch {
case config.IsEIP155(blockNumber):
signer = NewEIP155Signer(config.ChainId)
case config.IsHomestead(blockNumber):
signer = HomesteadSigner{}
default:
signer = FrontierSigner{}
}
return signer
}
// SignECDSA signs the transaction using the given signer and private key
func SignECDSA(s Signer, tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
h := s.Hash(tx)
sig, err := crypto.Sign(h[:], prv)
if err != nil {
return nil, err
}
return s.WithSignature(tx, sig)
}
// From derives the sender from the tx using the signer derivation
// functions.
// From returns the address derived from the signature (V, R, S) using secp256k1
// elliptic curve and an error if it failed deriving or upon an incorrect
// signature.
//
// From may cache the address, allowing it to be used regardless of
// signing method.
func From(signer Signer, tx *Transaction, cache bool) (common.Address, error) {
if from := tx.from.Load(); from != nil {
return from.(common.Address), nil
}
pubkey, err := signer.PublicKey(tx)
if err != nil {
return common.Address{}, err
}
var addr common.Address
copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
if cache {
tx.from.Store(addr)
}
return addr, nil
}
// SignatureValues returns the ECDSA signature values contained in the transaction.
func SignatureValues(signer Signer, tx *Transaction) (v byte, r *big.Int, s *big.Int) {
return normaliseV(signer, tx.data.V), new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
}
type Signer interface {
// Hash returns the rlp encoded hash for signatures
Hash(tx *Transaction) common.Hash
// PubilcKey returns the public key derived from the signature
PublicKey(tx *Transaction) ([]byte, error)
// SignECDSA signs the transaction with the given and returns a copy of the tx
SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error)
// WithSignature returns a copy of the transaction with the given signature
WithSignature(tx *Transaction, sig []byte) (*Transaction, error)
}
// EIP155Transaction implements TransactionInterface using the
// EIP155 rules
type EIP155Signer struct {
HomesteadSigner
chainId, chainIdMul *big.Int
}
func NewEIP155Signer(chainId *big.Int) EIP155Signer {
return EIP155Signer{
chainId: chainId,
chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)),
}
}
func (s EIP155Signer) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
return SignECDSA(s, tx, prv)
}
func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) {
// if the transaction is not protected fall back to homestead signer
if !tx.Protected() {
return (HomesteadSigner{}).PublicKey(tx)
}
if tx.ChainId().Cmp(s.chainId) != 0 {
return nil, ErrInvalidChainId
}
V := normaliseV(s, tx.data.V)
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
return nil, ErrInvalidSig
}
// encode the signature in uncompressed format
R, S := tx.data.R.Bytes(), tx.data.S.Bytes()
sig := make([]byte, 65)
copy(sig[32-len(R):32], R)
copy(sig[64-len(S):64], S)
sig[64] = V - 27
// recover the public key from the signature
hash := s.Hash(tx)
pub, err := crypto.Ecrecover(hash[:], sig)
if err != nil {
return nil, err
}
if len(pub) == 0 || pub[0] != 4 {
return nil, errors.New("invalid public key")
}
return pub, nil
}
// WithSignature returns a new transaction with the given signature.
// This signature needs to be formatted as described in the yellow paper (v+27).
func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
if len(sig) != 65 {
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
}
cpy := &Transaction{signer: tx.signer, data: tx.data}
cpy.data.R = new(big.Int).SetBytes(sig[:32])
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
if s.chainId.BitLen() > 0 {
cpy.data.V = big.NewInt(int64(sig[64] + 35))
cpy.data.V.Add(cpy.data.V, s.chainIdMul)
}
return cpy, nil
}
// Hash returns the hash to be signed by the sender.
// It does not uniquely identify the transaction.
func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
tx.data.AccountNonce,
tx.data.Price,
tx.data.GasLimit,
tx.data.Recipient,
tx.data.Amount,
tx.data.Payload,
s.chainId, uint(0), uint(0),
})
}
func (s EIP155Signer) SigECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
h := s.Hash(tx)
sig, err := crypto.Sign(h[:], prv)
if err != nil {
return nil, err
}
return s.WithSignature(tx, sig)
}
// HomesteadTransaction implements TransactionInterface using the
// homestead rules.
type HomesteadSigner struct{}
// WithSignature returns a new transaction with the given snature.
// This snature needs to be formatted as described in the yellow paper (v+27).
func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
if len(sig) != 65 {
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
}
cpy := &Transaction{signer: tx.signer, data: tx.data}
cpy.data.R = new(big.Int).SetBytes(sig[:32])
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
return cpy, nil
}
func (hs HomesteadSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
h := hs.Hash(tx)
sig, err := crypto.Sign(h[:], prv)
if err != nil {
return nil, err
}
return hs.WithSignature(tx, sig)
}
func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) {
if tx.data.V.BitLen() > 8 {
return nil, ErrInvalidSig
}
V := byte(tx.data.V.Uint64())
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
return nil, ErrInvalidSig
}
// encode the snature in uncompressed format
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
sig := make([]byte, 65)
copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
sig[64] = V - 27
// recover the public key from the snature
hash := hs.Hash(tx)
pub, err := crypto.Ecrecover(hash[:], sig)
if err != nil {
return nil, err
}
if len(pub) == 0 || pub[0] != 4 {
return nil, errors.New("invalid public key")
}
return pub, nil
}
// Hash returns the hash to be sned by the sender.
// It does not uniquely identify the transaction.
func (hs HomesteadSigner) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
tx.data.AccountNonce,
tx.data.Price,
tx.data.GasLimit,
tx.data.Recipient,
tx.data.Amount,
tx.data.Payload,
})
}
type FrontierSigner struct{}
// WithSignature returns a new transaction with the given snature.
// This snature needs to be formatted as described in the yellow paper (v+27).
func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
if len(sig) != 65 {
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
}
cpy := &Transaction{signer: tx.signer, data: tx.data}
cpy.data.R = new(big.Int).SetBytes(sig[:32])
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
return cpy, nil
}
func (fs FrontierSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
h := fs.Hash(tx)
sig, err := crypto.Sign(h[:], prv)
if err != nil {
return nil, err
}
return fs.WithSignature(tx, sig)
}
// Hash returns the hash to be sned by the sender.
// It does not uniquely identify the transaction.
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
return rlpHash([]interface{}{
tx.data.AccountNonce,
tx.data.Price,
tx.data.GasLimit,
tx.data.Recipient,
tx.data.Amount,
tx.data.Payload,
})
}
func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) {
if tx.data.V.BitLen() > 8 {
return nil, ErrInvalidSig
}
V := byte(tx.data.V.Uint64())
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) {
return nil, ErrInvalidSig
}
// encode the snature in uncompressed format
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
sig := make([]byte, 65)
copy(sig[32-len(r):32], r)
copy(sig[64-len(s):64], s)
sig[64] = V - 27
// recover the public key from the snature
hash := fs.Hash(tx)
pub, err := crypto.Ecrecover(hash[:], sig)
if err != nil {
return nil, err
}
if len(pub) == 0 || pub[0] != 4 {
return nil, errors.New("invalid public key")
}
return pub, nil
}
// normaliseV returns the Ethereum version of the V parameter
func normaliseV(s Signer, v *big.Int) byte {
if s, ok := s.(EIP155Signer); ok {
stdV := v.BitLen() <= 8 && (v.Uint64() == 27 || v.Uint64() == 28)
if s.chainId.BitLen() > 0 && !stdV {
nv := byte((new(big.Int).Sub(v, s.chainIdMul).Uint64()) - 35 + 27)
return nv
}
}
return byte(v.Uint64())
}
// deriveChainId derives the chain id from the given v parameter
func deriveChainId(v *big.Int) *big.Int {
if v.BitLen() <= 64 {
v := v.Uint64()
if v == 27 || v == 28 {
return new(big.Int)
}
return new(big.Int).SetUint64((v - 35) / 2)
}
v = new(big.Int).Sub(v, big.NewInt(35))
return v.Div(v, big.NewInt(2))
}

View File

@@ -0,0 +1,33 @@
package types
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestChainId(t *testing.T) {
key, _ := defaultTestKey()
tx := NewTransaction(0, common.Address{}, new(big.Int), new(big.Int), new(big.Int), nil)
tx.SetSigner(NewEIP155Signer(big.NewInt(1)))
var err error
tx, err = tx.SignECDSA(key)
if err != nil {
t.Fatal(err)
}
tx.SetSigner(NewEIP155Signer(big.NewInt(2)))
_, err = tx.From()
if err != ErrInvalidChainId {
t.Error("expected error:", ErrInvalidChainId)
}
tx.SetSigner(NewEIP155Signer(big.NewInt(1)))
_, err = tx.From()
if err != nil {
t.Error("expected no error")
}
}

View File

@@ -128,15 +128,25 @@ func TestTransactionPriceNonceSort(t *testing.T) {
keys[i], _ = crypto.GenerateKey()
}
// Generate a batch of transactions with overlapping values, but shifted nonces
txs := []*Transaction{}
groups := map[common.Address]Transactions{}
for start, key := range keys {
addr := crypto.PubkeyToAddress(key.PublicKey)
for i := 0; i < 25; i++ {
tx, _ := NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil).SignECDSA(key)
txs = append(txs, tx)
groups[addr] = append(groups[addr], tx)
}
}
// Sort the transactions and cross check the nonce ordering
SortByPriceAndNonce(txs)
txset := NewTransactionsByPriceAndNonce(groups)
txs := Transactions{}
for {
if tx := txset.Peek(); tx != nil {
txs = append(txs, tx)
txset.Shift()
}
break
}
for i, txi := range txs {
fromi, _ := txi.From()

View File

@@ -27,7 +27,7 @@ type ContractRef interface {
ReturnGas(*big.Int, *big.Int)
Address() common.Address
Value() *big.Int
SetCode([]byte)
SetCode(common.Hash, []byte)
ForEachStorage(callback func(key, value common.Hash) bool)
}
@@ -44,8 +44,9 @@ type Contract struct {
jumpdests destinations // result of JUMPDEST analysis.
Code []byte
Input []byte
CodeHash common.Hash
CodeAddr *common.Address
Input []byte
value, Gas, UsedGas, Price *big.Int
@@ -143,14 +144,16 @@ func (c *Contract) Value() *big.Int {
}
// SetCode sets the code to the contract
func (self *Contract) SetCode(code []byte) {
func (self *Contract) SetCode(hash common.Hash, code []byte) {
self.Code = code
self.CodeHash = hash
}
// SetCallCode sets the code of the contract and address of the backing data
// object
func (self *Contract) SetCallCode(addr *common.Address, code []byte) {
func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
self.Code = code
self.CodeHash = hash
self.CodeAddr = addr
}

View File

@@ -95,7 +95,7 @@ func ecrecoverFunc(in []byte) []byte {
// tighter sig s values in homestead only apply to tx sigs
if !crypto.ValidateSignatureValues(v, r, s, false) {
glog.V(logger.Debug).Infof("EC RECOVER FAIL: v, r or s value invalid")
glog.V(logger.Detail).Infof("ECRECOVER error: v, r or s value invalid")
return nil
}
@@ -106,7 +106,7 @@ func ecrecoverFunc(in []byte) []byte {
pubKey, err := crypto.Ecrecover(in[:32], rsv)
// make sure the public key is a valid one
if err != nil {
glog.V(logger.Error).Infof("EC RECOVER FAIL: ", err)
glog.V(logger.Detail).Infoln("ECRECOVER error: ", err)
return nil
}

View File

@@ -20,25 +20,20 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/params"
)
// RuleSet is an interface that defines the current rule set during the
// execution of the EVM instructions (e.g. whether it's homestead)
type RuleSet interface {
IsHomestead(*big.Int) bool
}
// Environment is an EVM requirement and helper which allows access to outside
// information such as states.
type Environment interface {
// The current ruleset
RuleSet() RuleSet
ChainConfig() *params.ChainConfig
// The state database
Db() Database
// Creates a restorable snapshot
MakeSnapshot() Database
SnapshotDatabase() int
// Set database to previous snapshot
SetSnapshot(Database)
RevertToSnapshot(int)
// Address of the original invoker (first occurrence of the VM invoker)
Origin() common.Address
// The block number this VM is invoked on
@@ -94,6 +89,8 @@ type Database interface {
GetNonce(common.Address) uint64
SetNonce(common.Address, uint64)
GetCodeHash(common.Address) common.Hash
GetCodeSize(common.Address) int
GetCode(common.Address) []byte
SetCode(common.Address, []byte)
@@ -103,9 +100,13 @@ type Database interface {
GetState(common.Address, common.Hash) common.Hash
SetState(common.Address, common.Hash, common.Hash)
Delete(common.Address) bool
Suicide(common.Address) bool
HasSuicided(common.Address) bool
// Exist reports whether the given account exists in state.
// Notably this should also return true for suicided accounts.
Exist(common.Address) bool
IsDeleted(common.Address) bool
Empty(common.Address) bool
}
// Account represents a contract or basic ethereum account.
@@ -117,7 +118,7 @@ type Account interface {
Balance() *big.Int
Address() common.Address
ReturnGas(*big.Int, *big.Int)
SetCode([]byte)
SetCode(common.Hash, []byte)
ForEachStorage(cb func(key, value common.Hash) bool)
Value() *big.Int
}

View File

@@ -35,8 +35,27 @@ var (
GasStop = big.NewInt(0)
GasContractByte = big.NewInt(200)
n64 = big.NewInt(64)
)
// calcGas returns the actual gas cost of the call.
//
// The cost of gas was changed during the homestead price change HF. To allow for EIP150
// to be implemented. The returned gas is gas - base * 63 / 64.
func callGas(gasTable params.GasTable, availableGas, base, callCost *big.Int) *big.Int {
if gasTable.CreateBySuicide != nil {
availableGas = new(big.Int).Sub(availableGas, base)
g := new(big.Int).Div(availableGas, n64)
g.Sub(availableGas, g)
if g.Cmp(callCost) < 0 {
return g
}
}
return callCost
}
// baseCheck checks for any stack error underflows
func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
// PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
@@ -127,18 +146,19 @@ var _baseCheck = map[OpCode]req{
MSIZE: {0, GasQuickStep, 1},
GAS: {0, GasQuickStep, 1},
BLOCKHASH: {1, GasExtStep, 1},
BALANCE: {1, GasExtStep, 1},
EXTCODESIZE: {1, GasExtStep, 1},
EXTCODECOPY: {4, GasExtStep, 0},
BALANCE: {1, Zero, 1},
EXTCODESIZE: {1, Zero, 1},
EXTCODECOPY: {4, Zero, 0},
SLOAD: {1, params.SloadGas, 1},
SSTORE: {2, Zero, 0},
SHA3: {2, params.Sha3Gas, 1},
CREATE: {3, params.CreateGas, 1},
CALL: {7, params.CallGas, 1},
CALLCODE: {7, params.CallGas, 1},
DELEGATECALL: {6, params.CallGas, 1},
JUMPDEST: {0, params.JumpdestGas, 0},
// Zero is calculated in the gasSwitch
CALL: {7, Zero, 1},
CALLCODE: {7, Zero, 1},
DELEGATECALL: {6, Zero, 1},
SUICIDE: {1, Zero, 0},
JUMPDEST: {0, params.JumpdestGas, 0},
RETURN: {2, Zero, 0},
PUSH1: {0, GasFastestStep, 1},
DUP1: {0, Zero, 1},

View File

@@ -363,7 +363,7 @@ func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Co
func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *stack) {
addr := common.BigToAddress(stack.pop())
l := big.NewInt(int64(len(env.Db().GetCode(addr))))
l := big.NewInt(int64(env.Db().GetCodeSize(addr)))
stack.push(l)
}
@@ -514,13 +514,18 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
input = memory.Get(offset.Int64(), size.Int64())
gas = new(big.Int).Set(contract.Gas)
)
contract.UseGas(contract.Gas)
if env.ChainConfig().IsEIP150(env.BlockNumber()) {
gas.Div(gas, n64)
gas = gas.Sub(contract.Gas, gas)
}
contract.UseGas(gas)
_, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
// Push item on the stack based on the returned error. If the ruleset is
// homestead we must check for CodeStoreOutOfGasError (homestead only
// rule) and treat as an error, if the ruleset is frontier we must
// ignore this error and pretend the operation was successful.
if env.RuleSet().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
if env.ChainConfig().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
stack.push(new(big.Int))
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
stack.push(new(big.Int))
@@ -614,7 +619,7 @@ func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contrac
balance := env.Db().GetBalance(contract.Address())
env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
env.Db().Delete(contract.Address())
env.Db().Suicide(contract.Address())
}
// following functions are used by the instruction jump table

View File

@@ -319,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
}()
}
homestead := env.RuleSet().IsHomestead(env.BlockNumber())
homestead := env.ChainConfig().IsHomestead(env.BlockNumber())
for pc < uint64(len(program.instructions)) {
instrCount++
@@ -425,7 +425,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
}
gas.Set(g)
case SUICIDE:
if !statedb.IsDeleted(contract.Address()) {
if !statedb.HasSuicided(contract.Address()) {
statedb.AddRefund(params.SuicideRefundGas)
}
case MLOAD:

View File

@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
)
const maxRun = 1000
@@ -135,7 +136,7 @@ func (account) SetNonce(uint64) {}
func (account) Balance() *big.Int { return nil }
func (account) Address() common.Address { return common.Address{} }
func (account) ReturnGas(*big.Int, *big.Int) {}
func (account) SetCode([]byte) {}
func (account) SetCode(common.Hash, []byte) {}
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
func runVmBench(test vmBench, b *testing.B) {
@@ -175,7 +176,9 @@ func NewEnv(noJit, forceJit bool) *Env {
return env
}
func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} }
func (self *Env) ChainConfig() *params.ChainConfig {
return params.TestChainConfig
}
func (self *Env) Vm() Vm { return self.evm }
func (self *Env) Origin() common.Address { return common.Address{} }
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
@@ -187,8 +190,8 @@ func (self *Env) StructLogs() []StructLog {
//func (self *Env) PrevHash() []byte { return self.parent }
func (self *Env) Coinbase() common.Address { return common.Address{} }
func (self *Env) MakeSnapshot() Database { return nil }
func (self *Env) SetSnapshot(Database) {}
func (self *Env) SnapshotDatabase() int { return 0 }
func (self *Env) RevertToSnapshot(int) {}
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
func (self *Env) Db() Database { return nil }

View File

@@ -16,7 +16,11 @@
package vm
import "math/big"
import (
"math/big"
"github.com/ethereum/go-ethereum/params"
)
type jumpPtr struct {
fn instrFn
@@ -25,7 +29,7 @@ type jumpPtr struct {
type vmJumpTable [256]jumpPtr
func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable {
func newJumpTable(ruleset *params.ChainConfig, blockNumber *big.Int) vmJumpTable {
var jumpTable vmJumpTable
// when initialising a new VM execution we must first check the homestead

View File

@@ -19,16 +19,18 @@ package vm
import (
"math/big"
"testing"
"github.com/ethereum/go-ethereum/params"
)
func TestInit(t *testing.T) {
jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(0))
jumpTable := newJumpTable(&params.ChainConfig{HomesteadBlock: big.NewInt(1)}, big.NewInt(0))
if jumpTable[DELEGATECALL].valid {
t.Error("Expected DELEGATECALL not to be present")
}
for _, n := range []int64{1, 2, 100} {
jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(n))
jumpTable := newJumpTable(&params.ChainConfig{HomesteadBlock: big.NewInt(1)}, big.NewInt(n))
if !jumpTable[DELEGATECALL].valid {
t.Error("Expected DELEGATECALL to be present for block", n)
}

View File

@@ -30,7 +30,7 @@ type dummyContractRef struct {
func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {}
func (dummyContractRef) Address() common.Address { return common.Address{} }
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
func (dummyContractRef) SetCode([]byte) {}
func (dummyContractRef) SetCode(common.Hash, []byte) {}
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
d.calledForEach = true
}

View File

@@ -23,13 +23,14 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
)
// Env is a basic runtime environment required for running the EVM.
type Env struct {
ruleSet vm.RuleSet
depth int
state *state.StateDB
chainConfig *params.ChainConfig
depth int
state *state.StateDB
origin common.Address
coinbase common.Address
@@ -49,14 +50,14 @@ type Env struct {
// NewEnv returns a new vm.Environment
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
env := &Env{
ruleSet: cfg.RuleSet,
state: state,
origin: cfg.Origin,
coinbase: cfg.Coinbase,
number: cfg.BlockNumber,
time: cfg.Time,
difficulty: cfg.Difficulty,
gasLimit: cfg.GasLimit,
chainConfig: cfg.ChainConfig,
state: state,
origin: cfg.Origin,
coinbase: cfg.Coinbase,
number: cfg.BlockNumber,
time: cfg.Time,
difficulty: cfg.Difficulty,
gasLimit: cfg.GasLimit,
}
env.evm = vm.New(env, vm.Config{
Debug: cfg.Debug,
@@ -79,16 +80,16 @@ func (self *Env) AddStructLog(log vm.StructLog) {
self.logs = append(self.logs, log)
}
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
func (self *Env) Vm() vm.Vm { return self.evm }
func (self *Env) Origin() common.Address { return self.origin }
func (self *Env) BlockNumber() *big.Int { return self.number }
func (self *Env) Coinbase() common.Address { return self.coinbase }
func (self *Env) Time() *big.Int { return self.time }
func (self *Env) Difficulty() *big.Int { return self.difficulty }
func (self *Env) Db() vm.Database { return self.state }
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig }
func (self *Env) Vm() vm.Vm { return self.evm }
func (self *Env) Origin() common.Address { return self.origin }
func (self *Env) BlockNumber() *big.Int { return self.number }
func (self *Env) Coinbase() common.Address { return self.coinbase }
func (self *Env) Time() *big.Int { return self.time }
func (self *Env) Difficulty() *big.Int { return self.difficulty }
func (self *Env) Db() vm.Database { return self.state }
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
func (self *Env) GetHash(n uint64) common.Hash {
return self.getHashFn(n)
}
@@ -100,11 +101,11 @@ func (self *Env) SetDepth(i int) { self.depth = i }
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0
}
func (self *Env) MakeSnapshot() vm.Database {
return self.state.Copy()
func (self *Env) SnapshotDatabase() int {
return self.state.Snapshot()
}
func (self *Env) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB))
func (self *Env) RevertToSnapshot(snapshot int) {
self.state.RevertToSnapshot(snapshot)
}
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {

View File

@@ -22,20 +22,23 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
// The default, always homestead, rule set for the vm env
type ruleSet struct{}
func (ruleSet) IsHomestead(*big.Int) bool { return true }
func (ruleSet) GasTable(*big.Int) params.GasTable {
return params.GasTableHomesteadGasRepriceFork
}
// Config is a basic type specifying certain configuration flags for running
// the EVM.
type Config struct {
RuleSet vm.RuleSet
ChainConfig *params.ChainConfig
Difficulty *big.Int
Origin common.Address
Coinbase common.Address
@@ -53,8 +56,8 @@ type Config struct {
// sets defaults on the config
func setDefaults(cfg *Config) {
if cfg.RuleSet == nil {
cfg.RuleSet = ruleSet{}
if cfg.ChainConfig == nil {
cfg.ChainConfig = params.TestChainConfig
}
if cfg.Difficulty == nil {
@@ -104,7 +107,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
receiver = cfg.State.CreateAccount(common.StringToAddress("contract"))
)
// set the receiver's (the executing contract) code for execution.
receiver.SetCode(code)
receiver.SetCode(crypto.Keccak256Hash(code), code)
// Call the code with the given configuration.
ret, err := vmenv.Call(

View File

@@ -16,10 +16,17 @@
package vm
import "math/big"
import (
"math/big"
"github.com/ethereum/go-ethereum/params"
)
type ruleSet struct {
hs *big.Int
}
func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 }
func (r ruleSet) GasTable(*big.Int) params.GasTable {
return params.GasTableHomestead
}

View File

@@ -44,8 +44,8 @@ type EVM struct {
env Environment
jumpTable vmJumpTable
cfg Config
logger *Logger
logger *Logger
gasTable params.GasTable
}
// New returns a new instance of the EVM.
@@ -57,9 +57,10 @@ func New(env Environment, cfg Config) *EVM {
return &EVM{
env: env,
jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()),
jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()),
cfg: cfg,
logger: logger,
gasTable: env.ChainConfig().GasTable(env.BlockNumber()),
}
}
@@ -79,10 +80,11 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
return nil, nil
}
var (
codehash = crypto.Keccak256Hash(contract.Code) // codehash is used when doing jump dest caching
program *Program
)
codehash := contract.CodeHash // codehash is used when doing jump dest caching
if codehash == (common.Hash{}) {
codehash = crypto.Keccak256Hash(contract.Code)
}
var program *Program
if evm.cfg.EnableJit {
// If the JIT is enabled check the status of the JIT program,
// if it doesn't exist compile a new program in a separate
@@ -175,8 +177,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
// Get the memory location of pc
op = contract.GetOp(pc)
//fmt.Printf("OP %d %v\n", op, op)
// calculate the new memory size and gas price for the current executing opcode
newMemSize, cost, err = calculateGasAndSize(evm.env, contract, caller, op, statedb, mem, stack)
newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack)
if err != nil {
return nil, err
}
@@ -241,7 +244,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
// the operation. This does not reduce gas or resizes the memory.
func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
var (
gas = new(big.Int)
newMemSize *big.Int = new(big.Int)
@@ -253,6 +256,34 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
// stack Check, memory resize & gas phase
switch op {
case SUICIDE:
// EIP150 homestead gas reprice fork:
if gasTable.CreateBySuicide != nil {
gas.Set(gasTable.Suicide)
var (
address = common.BigToAddress(stack.data[len(stack.data)-1])
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
)
if eip158 {
// if empty and transfers value
if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 {
gas.Add(gas, gasTable.CreateBySuicide)
}
} else if !env.Db().Exist(address) {
gas.Add(gas, gasTable.CreateBySuicide)
}
}
if !statedb.HasSuicided(contract.Address()) {
statedb.AddRefund(params.SuicideRefundGas)
}
case EXTCODESIZE:
gas.Set(gasTable.ExtcodeSize)
case BALANCE:
gas.Set(gasTable.Balance)
case SLOAD:
gas.Set(gasTable.SLoad)
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
n := int(op - SWAP1 + 2)
err := stack.require(n)
@@ -281,8 +312,11 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
newMemSize = calcMemSize(mStart, mSize)
quadMemGas(mem, newMemSize, gas)
case EXP:
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8)
gas.Add(gas, new(big.Int).Mul(big.NewInt(expByteLen), gasTable.ExpByte))
case SSTORE:
err := stack.require(2)
if err != nil {
@@ -309,67 +343,109 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
g = params.SstoreClearGas
}
gas.Set(g)
case SUICIDE:
if !statedb.IsDeleted(contract.Address()) {
statedb.AddRefund(params.SuicideRefundGas)
}
case MLOAD:
newMemSize = calcMemSize(stack.peek(), u256(32))
quadMemGas(mem, newMemSize, gas)
case MSTORE8:
newMemSize = calcMemSize(stack.peek(), u256(1))
quadMemGas(mem, newMemSize, gas)
case MSTORE:
newMemSize = calcMemSize(stack.peek(), u256(32))
quadMemGas(mem, newMemSize, gas)
case RETURN:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
quadMemGas(mem, newMemSize, gas)
case SHA3:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
words := toWordSize(stack.data[stack.len()-2])
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
quadMemGas(mem, newMemSize, gas)
case CALLDATACOPY:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
words := toWordSize(stack.data[stack.len()-3])
gas.Add(gas, words.Mul(words, params.CopyGas))
quadMemGas(mem, newMemSize, gas)
case CODECOPY:
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
words := toWordSize(stack.data[stack.len()-3])
gas.Add(gas, words.Mul(words, params.CopyGas))
quadMemGas(mem, newMemSize, gas)
case EXTCODECOPY:
gas.Set(gasTable.ExtcodeCopy)
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
words := toWordSize(stack.data[stack.len()-4])
gas.Add(gas, words.Mul(words, params.CopyGas))
quadMemGas(mem, newMemSize, gas)
case CREATE:
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
case CALL, CALLCODE:
gas.Add(gas, stack.data[stack.len()-1])
quadMemGas(mem, newMemSize, gas)
case CALL, CALLCODE:
gas.Set(gasTable.Calls)
transfersValue := stack.data[len(stack.data)-3].BitLen() > 0
if op == CALL {
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
var (
address = common.BigToAddress(stack.data[len(stack.data)-2])
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
)
if eip158 {
if env.Db().Empty(address) && transfersValue {
gas.Add(gas, params.CallNewAccountGas)
}
} else if !env.Db().Exist(address) {
gas.Add(gas, params.CallNewAccountGas)
}
}
if len(stack.data[stack.len()-3].Bytes()) > 0 {
if transfersValue {
gas.Add(gas, params.CallValueTransferGas)
}
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
newMemSize = common.BigMax(x, y)
quadMemGas(mem, newMemSize, gas)
cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
// Replace the stack item with the new gas calculation. This means that
// either the original item is left on the stack or the item is replaced by:
// (availableGas - gas) * 63 / 64
// We replace the stack item so that it's available when the opCall instruction is
// called. This information is otherwise lost due to the dependency on *current*
// available gas.
stack.data[stack.len()-1] = cg
gas.Add(gas, cg)
case DELEGATECALL:
gas.Add(gas, stack.data[stack.len()-1])
gas.Set(gasTable.Calls)
x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
newMemSize = common.BigMax(x, y)
quadMemGas(mem, newMemSize, gas)
cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
// Replace the stack item with the new gas calculation. This means that
// either the original item is left on the stack or the item is replaced by:
// (availableGas - gas) * 63 / 64
// We replace the stack item so that it's available when the opCall instruction is
// called.
stack.data[stack.len()-1] = cg
gas.Add(gas, cg)
}
quadMemGas(mem, newMemSize, gas)
return newMemSize, gas, nil
}

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/core/vm"
"github.com/ethereum/go-ethereum/params"
)
// GetHashFn returns a function for which the VM env can query block hashes through
@@ -41,11 +42,11 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
}
type VMEnv struct {
chainConfig *ChainConfig // Chain configuration
state *state.StateDB // State to use for executing
evm *vm.EVM // The Ethereum Virtual Machine
depth int // Current execution depth
msg Message // Message appliod
chainConfig *params.ChainConfig // Chain configuration
state *state.StateDB // State to use for executing
evm *vm.EVM // The Ethereum Virtual Machine
depth int // Current execution depth
msg Message // Message appliod
header *types.Header // Header information
chain *BlockChain // Blockchain handle
@@ -53,7 +54,7 @@ type VMEnv struct {
getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
}
func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
func NewEnv(state *state.StateDB, chainConfig *params.ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
env := &VMEnv{
chainConfig: chainConfig,
chain: chain,
@@ -72,18 +73,18 @@ func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, m
return env
}
func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig }
func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
func (self *VMEnv) Time() *big.Int { return self.header.Time }
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) Depth() int { return self.depth }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig }
func (self *VMEnv) Vm() vm.Vm { return self.evm }
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
func (self *VMEnv) Time() *big.Int { return self.header.Time }
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
func (self *VMEnv) Db() vm.Database { return self.state }
func (self *VMEnv) Depth() int { return self.depth }
func (self *VMEnv) SetDepth(i int) { self.depth = i }
func (self *VMEnv) GetHash(n uint64) common.Hash {
return self.getHashFn(n)
}
@@ -95,12 +96,12 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
return self.state.GetBalance(from).Cmp(balance) >= 0
}
func (self *VMEnv) MakeSnapshot() vm.Database {
return self.state.Copy()
func (self *VMEnv) SnapshotDatabase() int {
return self.state.Snapshot()
}
func (self *VMEnv) SetSnapshot(copy vm.Database) {
self.state.Set(copy.(*state.StateDB))
func (self *VMEnv) RevertToSnapshot(snapshot int) {
self.state.RevertToSnapshot(snapshot)
}
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {

View File

@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !amd64 appengine gccgo
package sha3
// rc stores the round constants for use in the ι step.

View File

@@ -0,0 +1,13 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64,!appengine,!gccgo
package sha3
// This function is implemented in keccakf_amd64.s.
//go:noescape
func keccakF1600(state *[25]uint64)

392
crypto/sha3/keccakf_amd64.s Normal file
View File

@@ -0,0 +1,392 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64,!appengine,!gccgo
// This code was translated into a form compatible with 6a from the public
// domain sources at https://github.com/gvanas/KeccakCodePackage
// Offsets in state
#define _ba (0*8)
#define _be (1*8)
#define _bi (2*8)
#define _bo (3*8)
#define _bu (4*8)
#define _ga (5*8)
#define _ge (6*8)
#define _gi (7*8)
#define _go (8*8)
#define _gu (9*8)
#define _ka (10*8)
#define _ke (11*8)
#define _ki (12*8)
#define _ko (13*8)
#define _ku (14*8)
#define _ma (15*8)
#define _me (16*8)
#define _mi (17*8)
#define _mo (18*8)
#define _mu (19*8)
#define _sa (20*8)
#define _se (21*8)
#define _si (22*8)
#define _so (23*8)
#define _su (24*8)
// Temporary registers
#define rT1 AX
// Round vars
#define rpState DI
#define rpStack SP
#define rDa BX
#define rDe CX
#define rDi DX
#define rDo R8
#define rDu R9
#define rBa R10
#define rBe R11
#define rBi R12
#define rBo R13
#define rBu R14
#define rCa SI
#define rCe BP
#define rCi rBi
#define rCo rBo
#define rCu R15
#define MOVQ_RBI_RCE MOVQ rBi, rCe
#define XORQ_RT1_RCA XORQ rT1, rCa
#define XORQ_RT1_RCE XORQ rT1, rCe
#define XORQ_RBA_RCU XORQ rBa, rCu
#define XORQ_RBE_RCU XORQ rBe, rCu
#define XORQ_RDU_RCU XORQ rDu, rCu
#define XORQ_RDA_RCA XORQ rDa, rCa
#define XORQ_RDE_RCE XORQ rDe, rCe
#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \
/* Prepare round */ \
MOVQ rCe, rDa; \
ROLQ $1, rDa; \
\
MOVQ _bi(iState), rCi; \
XORQ _gi(iState), rDi; \
XORQ rCu, rDa; \
XORQ _ki(iState), rCi; \
XORQ _mi(iState), rDi; \
XORQ rDi, rCi; \
\
MOVQ rCi, rDe; \
ROLQ $1, rDe; \
\
MOVQ _bo(iState), rCo; \
XORQ _go(iState), rDo; \
XORQ rCa, rDe; \
XORQ _ko(iState), rCo; \
XORQ _mo(iState), rDo; \
XORQ rDo, rCo; \
\
MOVQ rCo, rDi; \
ROLQ $1, rDi; \
\
MOVQ rCu, rDo; \
XORQ rCe, rDi; \
ROLQ $1, rDo; \
\
MOVQ rCa, rDu; \
XORQ rCi, rDo; \
ROLQ $1, rDu; \
\
/* Result b */ \
MOVQ _ba(iState), rBa; \
MOVQ _ge(iState), rBe; \
XORQ rCo, rDu; \
MOVQ _ki(iState), rBi; \
MOVQ _mo(iState), rBo; \
MOVQ _su(iState), rBu; \
XORQ rDe, rBe; \
ROLQ $44, rBe; \
XORQ rDi, rBi; \
XORQ rDa, rBa; \
ROLQ $43, rBi; \
\
MOVQ rBe, rCa; \
MOVQ rc, rT1; \
ORQ rBi, rCa; \
XORQ rBa, rT1; \
XORQ rT1, rCa; \
MOVQ rCa, _ba(oState); \
\
XORQ rDu, rBu; \
ROLQ $14, rBu; \
MOVQ rBa, rCu; \
ANDQ rBe, rCu; \
XORQ rBu, rCu; \
MOVQ rCu, _bu(oState); \
\
XORQ rDo, rBo; \
ROLQ $21, rBo; \
MOVQ rBo, rT1; \
ANDQ rBu, rT1; \
XORQ rBi, rT1; \
MOVQ rT1, _bi(oState); \
\
NOTQ rBi; \
ORQ rBa, rBu; \
ORQ rBo, rBi; \
XORQ rBo, rBu; \
XORQ rBe, rBi; \
MOVQ rBu, _bo(oState); \
MOVQ rBi, _be(oState); \
B_RBI_RCE; \
\
/* Result g */ \
MOVQ _gu(iState), rBe; \
XORQ rDu, rBe; \
MOVQ _ka(iState), rBi; \
ROLQ $20, rBe; \
XORQ rDa, rBi; \
ROLQ $3, rBi; \
MOVQ _bo(iState), rBa; \
MOVQ rBe, rT1; \
ORQ rBi, rT1; \
XORQ rDo, rBa; \
MOVQ _me(iState), rBo; \
MOVQ _si(iState), rBu; \
ROLQ $28, rBa; \
XORQ rBa, rT1; \
MOVQ rT1, _ga(oState); \
G_RT1_RCA; \
\
XORQ rDe, rBo; \
ROLQ $45, rBo; \
MOVQ rBi, rT1; \
ANDQ rBo, rT1; \
XORQ rBe, rT1; \
MOVQ rT1, _ge(oState); \
G_RT1_RCE; \
\
XORQ rDi, rBu; \
ROLQ $61, rBu; \
MOVQ rBu, rT1; \
ORQ rBa, rT1; \
XORQ rBo, rT1; \
MOVQ rT1, _go(oState); \
\
ANDQ rBe, rBa; \
XORQ rBu, rBa; \
MOVQ rBa, _gu(oState); \
NOTQ rBu; \
G_RBA_RCU; \
\
ORQ rBu, rBo; \
XORQ rBi, rBo; \
MOVQ rBo, _gi(oState); \
\
/* Result k */ \
MOVQ _be(iState), rBa; \
MOVQ _gi(iState), rBe; \
MOVQ _ko(iState), rBi; \
MOVQ _mu(iState), rBo; \
MOVQ _sa(iState), rBu; \
XORQ rDi, rBe; \
ROLQ $6, rBe; \
XORQ rDo, rBi; \
ROLQ $25, rBi; \
MOVQ rBe, rT1; \
ORQ rBi, rT1; \
XORQ rDe, rBa; \
ROLQ $1, rBa; \
XORQ rBa, rT1; \
MOVQ rT1, _ka(oState); \
K_RT1_RCA; \
\
XORQ rDu, rBo; \
ROLQ $8, rBo; \
MOVQ rBi, rT1; \
ANDQ rBo, rT1; \
XORQ rBe, rT1; \
MOVQ rT1, _ke(oState); \
K_RT1_RCE; \
\
XORQ rDa, rBu; \
ROLQ $18, rBu; \
NOTQ rBo; \
MOVQ rBo, rT1; \
ANDQ rBu, rT1; \
XORQ rBi, rT1; \
MOVQ rT1, _ki(oState); \
\
MOVQ rBu, rT1; \
ORQ rBa, rT1; \
XORQ rBo, rT1; \
MOVQ rT1, _ko(oState); \
\
ANDQ rBe, rBa; \
XORQ rBu, rBa; \
MOVQ rBa, _ku(oState); \
K_RBA_RCU; \
\
/* Result m */ \
MOVQ _ga(iState), rBe; \
XORQ rDa, rBe; \
MOVQ _ke(iState), rBi; \
ROLQ $36, rBe; \
XORQ rDe, rBi; \
MOVQ _bu(iState), rBa; \
ROLQ $10, rBi; \
MOVQ rBe, rT1; \
MOVQ _mi(iState), rBo; \
ANDQ rBi, rT1; \
XORQ rDu, rBa; \
MOVQ _so(iState), rBu; \
ROLQ $27, rBa; \
XORQ rBa, rT1; \
MOVQ rT1, _ma(oState); \
M_RT1_RCA; \
\
XORQ rDi, rBo; \
ROLQ $15, rBo; \
MOVQ rBi, rT1; \
ORQ rBo, rT1; \
XORQ rBe, rT1; \
MOVQ rT1, _me(oState); \
M_RT1_RCE; \
\
XORQ rDo, rBu; \
ROLQ $56, rBu; \
NOTQ rBo; \
MOVQ rBo, rT1; \
ORQ rBu, rT1; \
XORQ rBi, rT1; \
MOVQ rT1, _mi(oState); \
\
ORQ rBa, rBe; \
XORQ rBu, rBe; \
MOVQ rBe, _mu(oState); \
\
ANDQ rBa, rBu; \
XORQ rBo, rBu; \
MOVQ rBu, _mo(oState); \
M_RBE_RCU; \
\
/* Result s */ \
MOVQ _bi(iState), rBa; \
MOVQ _go(iState), rBe; \
MOVQ _ku(iState), rBi; \
XORQ rDi, rBa; \
MOVQ _ma(iState), rBo; \
ROLQ $62, rBa; \
XORQ rDo, rBe; \
MOVQ _se(iState), rBu; \
ROLQ $55, rBe; \
\
XORQ rDu, rBi; \
MOVQ rBa, rDu; \
XORQ rDe, rBu; \
ROLQ $2, rBu; \
ANDQ rBe, rDu; \
XORQ rBu, rDu; \
MOVQ rDu, _su(oState); \
\
ROLQ $39, rBi; \
S_RDU_RCU; \
NOTQ rBe; \
XORQ rDa, rBo; \
MOVQ rBe, rDa; \
ANDQ rBi, rDa; \
XORQ rBa, rDa; \
MOVQ rDa, _sa(oState); \
S_RDA_RCA; \
\
ROLQ $41, rBo; \
MOVQ rBi, rDe; \
ORQ rBo, rDe; \
XORQ rBe, rDe; \
MOVQ rDe, _se(oState); \
S_RDE_RCE; \
\
MOVQ rBo, rDi; \
MOVQ rBu, rDo; \
ANDQ rBu, rDi; \
ORQ rBa, rDo; \
XORQ rBi, rDi; \
XORQ rBo, rDo; \
MOVQ rDi, _si(oState); \
MOVQ rDo, _so(oState) \
// func keccakF1600(state *[25]uint64)
TEXT ·keccakF1600(SB), 0, $200-8
MOVQ state+0(FP), rpState
SUBQ $(8*25), SP
// Convert the user state into an internal state
NOTQ _be(rpState)
NOTQ _bi(rpState)
NOTQ _go(rpState)
NOTQ _ki(rpState)
NOTQ _mi(rpState)
NOTQ _sa(rpState)
// Execute the KeccakF permutation
MOVQ _ba(rpState), rCa
MOVQ _be(rpState), rCe
MOVQ _bu(rpState), rCu
XORQ _ga(rpState), rCa
XORQ _ge(rpState), rCe
XORQ _gu(rpState), rCu
XORQ _ka(rpState), rCa
XORQ _ke(rpState), rCe
XORQ _ku(rpState), rCu
XORQ _ma(rpState), rCa
XORQ _me(rpState), rCe
XORQ _mu(rpState), rCu
XORQ _sa(rpState), rCa
XORQ _se(rpState), rCe
MOVQ _si(rpState), rDi
MOVQ _so(rpState), rDo
XORQ _su(rpState), rCu
mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP)
// Revert the internal state to the user state
NOTQ _be(rpState)
NOTQ _bi(rpState)
NOTQ _go(rpState)
NOTQ _ki(rpState)
NOTQ _mi(rpState)
NOTQ _sa(rpState)
ADDQ $(8*25), SP
RET

View File

@@ -42,7 +42,7 @@ type state struct {
storage [maxRate]byte
// Specific to SHA-3 and SHAKE.
fixedOutput bool // whether this is a fixed-ouput-length instance
fixedOutput bool // whether this is a fixed-output-length instance
outputLen int // the default output size in bytes
state spongeDirection // whether the sponge is absorbing or squeezing
}

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build !amd64,!386 appengine
// +build !amd64,!386,!ppc64le appengine
package sha3

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// +build amd64 386
// +build amd64 386 ppc64le
// +build !appengine
package sha3

View File

@@ -46,6 +46,7 @@ import (
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/syndtr/goleveldb/leveldb"
@@ -324,22 +325,18 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string][]*RPCTrans
// Flatten the pending transactions
for account, batches := range pending {
dump := make(map[string][]*RPCTransaction)
for nonce, txs := range batches {
nonce := fmt.Sprintf("%d", nonce)
for _, tx := range txs {
dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx))
}
for _, tx := range batches {
nonce := fmt.Sprintf("%d", tx.Nonce())
dump[nonce] = []*RPCTransaction{newRPCPendingTransaction(tx)}
}
content["pending"][account.Hex()] = dump
}
// Flatten the queued transactions
for account, batches := range queue {
dump := make(map[string][]*RPCTransaction)
for nonce, txs := range batches {
nonce := fmt.Sprintf("%d", nonce)
for _, tx := range txs {
dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx))
}
for _, tx := range batches {
nonce := fmt.Sprintf("%d", tx.Nonce())
dump[nonce] = []*RPCTransaction{newRPCPendingTransaction(tx)}
}
content["queued"][account.Hex()] = dump
}
@@ -374,22 +371,18 @@ func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string][]string {
// Flatten the pending transactions
for account, batches := range pending {
dump := make(map[string][]string)
for nonce, txs := range batches {
nonce := fmt.Sprintf("%d", nonce)
for _, tx := range txs {
dump[nonce] = append(dump[nonce], format(tx))
}
for _, tx := range batches {
nonce := fmt.Sprintf("%d", tx.Nonce())
dump[nonce] = []string{format(tx)}
}
content["pending"][account.Hex()] = dump
}
// Flatten the queued transactions
for account, batches := range queue {
dump := make(map[string][]string)
for nonce, txs := range batches {
nonce := fmt.Sprintf("%d", nonce)
for _, tx := range txs {
dump[nonce] = append(dump[nonce], format(tx))
}
for _, tx := range batches {
nonce := fmt.Sprintf("%d", tx.Nonce())
dump[nonce] = []string{format(tx)}
}
content["queued"][account.Hex()] = dump
}
@@ -416,6 +409,7 @@ func (s *PublicAccountAPI) Accounts() []accounts.Account {
// It offers methods to create, (un)lock en list accounts. Some methods accept
// passwords and are therefore considered private by default.
type PrivateAccountAPI struct {
bc *core.BlockChain
am *accounts.Manager
txPool *core.TxPool
txMu *sync.Mutex
@@ -425,6 +419,7 @@ type PrivateAccountAPI struct {
// NewPrivateAccountAPI create a new PrivateAccountAPI.
func NewPrivateAccountAPI(e *Ethereum) *PrivateAccountAPI {
return &PrivateAccountAPI{
bc: e.blockchain,
am: e.accountManager,
txPool: e.txPool,
txMu: &e.txMu,
@@ -503,6 +498,10 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(args SendTxArgs, passwd strin
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
}
if s.bc.Config().IsEIP155(s.bc.CurrentBlock().Number()) {
tx.SetSigner(types.NewEIP155Signer(s.bc.Config().ChainId))
}
signature, err := s.am.SignWithPassphrase(args.From, passwd, tx.SigHash().Bytes())
if err != nil {
return common.Hash{}, err
@@ -514,7 +513,7 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(args SendTxArgs, passwd strin
// PublicBlockChainAPI provides an API to access the Ethereum blockchain.
// It offers only methods that operate on public data that is freely available to anyone.
type PublicBlockChainAPI struct {
config *core.ChainConfig
config *params.ChainConfig
bc *core.BlockChain
chainDb ethdb.Database
eventMux *event.TypeMux
@@ -526,7 +525,7 @@ type PublicBlockChainAPI struct {
}
// NewPublicBlockChainAPI creates a new Etheruem blockchain API.
func NewPublicBlockChainAPI(config *core.ChainConfig, bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, gpo *GasPriceOracle, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI {
func NewPublicBlockChainAPI(config *params.ChainConfig, bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, gpo *GasPriceOracle, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI {
api := &PublicBlockChainAPI{
config: config,
bc: bc,
@@ -777,7 +776,7 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st
}
// Execute the call and return
vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), s.config.VmConfig)
vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), vm.Config{})
gp := new(core.GasPool).AddGas(common.MaxBig)
res, requiredGas, _, err := core.NewStateTransition(vmenv, msg, gp).TransitionDb()
@@ -831,6 +830,9 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
if fullTx {
formatTx = func(tx *types.Transaction) (interface{}, error) {
if tx.Protected() {
tx.SetSigner(types.NewEIP155Signer(s.bc.Config().ChainId))
}
return newRPCTransaction(b, tx.Hash())
}
}
@@ -987,7 +989,7 @@ func getTransaction(chainDb ethdb.Database, txPool *core.TxPool, txHash common.H
}
} else {
// pending transaction?
tx = txPool.GetTransaction(txHash)
tx = txPool.Get(txHash)
isPending = true
}
@@ -1215,6 +1217,10 @@ func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
}
if s.bc.Config().IsEIP155(s.bc.CurrentBlock().Number()) {
tx.SetSigner(types.NewEIP155Signer(s.bc.Config().ChainId))
}
signature, err := s.am.Sign(args.From, tx.SigHash().Bytes())
if err != nil {
return common.Hash{}, err
@@ -1399,12 +1405,13 @@ func (s *PublicTransactionPoolAPI) SignTransaction(args SignTransactionArgs) (*S
// PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of
// the accounts this node manages.
func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
pending := s.txPool.GetTransactions()
pending := s.txPool.Pending()
transactions := make([]*RPCTransaction, 0, len(pending))
for _, tx := range pending {
from, _ := tx.FromFrontier()
if s.am.HasAddress(from) {
transactions = append(transactions, newRPCPendingTransaction(tx))
for addr, txs := range pending {
if s.am.HasAddress(addr) {
for _, tx := range txs {
transactions = append(transactions, newRPCPendingTransaction(tx))
}
}
}
return transactions
@@ -1438,35 +1445,36 @@ func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (
// Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the
// pool and reinsert it with the new gas price and limit.
func (s *PublicTransactionPoolAPI) Resend(tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) {
pending := s.txPool.Pending()
for addr, txs := range pending {
for _, p := range txs {
if addr == tx.From && p.SigHash() == tx.tx.SigHash() {
if gasPrice == nil {
gasPrice = rpc.NewHexNumber(tx.tx.GasPrice())
}
if gasLimit == nil {
gasLimit = rpc.NewHexNumber(tx.tx.Gas())
}
pending := s.txPool.GetTransactions()
for _, p := range pending {
if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() {
if gasPrice == nil {
gasPrice = rpc.NewHexNumber(tx.tx.GasPrice())
}
if gasLimit == nil {
gasLimit = rpc.NewHexNumber(tx.tx.Gas())
}
var newTx *types.Transaction
if tx.tx.To() == nil {
newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
} else {
newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
}
var newTx *types.Transaction
if tx.tx.To() == nil {
newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
} else {
newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
}
signedTx, err := s.sign(tx.From, newTx)
if err != nil {
return common.Hash{}, err
}
signedTx, err := s.sign(tx.From, newTx)
if err != nil {
return common.Hash{}, err
}
s.txPool.Remove(tx.Hash)
if err = s.txPool.Add(signedTx); err != nil {
return common.Hash{}, err
}
s.txPool.RemoveTx(tx.Hash)
if err = s.txPool.Add(signedTx); err != nil {
return common.Hash{}, err
return signedTx.Hash(), nil
}
return signedTx.Hash(), nil
}
}
@@ -1575,14 +1583,14 @@ func NewPublicDebugAPI(eth *Ethereum) *PublicDebugAPI {
}
// DumpBlock retrieves the entire state of the database at a given block.
func (api *PublicDebugAPI) DumpBlock(number uint64) (state.World, error) {
func (api *PublicDebugAPI) DumpBlock(number uint64) (state.Dump, error) {
block := api.eth.BlockChain().GetBlockByNumber(number)
if block == nil {
return state.World{}, fmt.Errorf("block #%d not found", number)
return state.Dump{}, fmt.Errorf("block #%d not found", number)
}
stateDb, err := state.New(block.Root(), api.eth.ChainDb())
stateDb, err := api.eth.BlockChain().StateAt(block.Root())
if err != nil {
return state.World{}, err
return state.Dump{}, err
}
return stateDb.RawDump(), nil
}
@@ -1625,13 +1633,13 @@ func (api *PublicDebugAPI) SeedHash(number uint64) (string, error) {
// PrivateDebugAPI is the collection of Etheruem APIs exposed over the private
// debugging endpoint.
type PrivateDebugAPI struct {
config *core.ChainConfig
config *params.ChainConfig
eth *Ethereum
}
// NewPrivateDebugAPI creates a new API definition for the private debug methods
// of the Ethereum service.
func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAPI {
func NewPrivateDebugAPI(config *params.ChainConfig, eth *Ethereum) *PrivateDebugAPI {
return &PrivateDebugAPI{config: config, eth: eth}
}
@@ -1748,7 +1756,7 @@ func (api *PrivateDebugAPI) traceBlock(block *types.Block, config *vm.Config) (b
if err := core.ValidateHeader(api.config, blockchain.AuxValidator(), block.Header(), blockchain.GetHeader(block.ParentHash()), true, false); err != nil {
return false, collector.traces, err
}
statedb, err := state.New(blockchain.GetBlock(block.ParentHash()).Root(), api.eth.ChainDb())
statedb, err := blockchain.StateAt(blockchain.GetBlock(block.ParentHash()).Root())
if err != nil {
return false, collector.traces, err
}
@@ -1850,7 +1858,7 @@ func (api *PrivateDebugAPI) TraceTransaction(txHash common.Hash, logger *vm.LogC
if parent == nil {
return nil, fmt.Errorf("block parent %x not found", block.ParentHash())
}
stateDb, err := state.New(parent.Root(), api.eth.ChainDb())
stateDb, err := api.eth.BlockChain().StateAt(parent.Root())
if err != nil {
return nil, err
}

View File

@@ -37,7 +37,6 @@ import (
"github.com/ethereum/go-ethereum/common/registrar/ethreg"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/ethdb"
@@ -47,6 +46,7 @@ import (
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
)
@@ -65,7 +65,7 @@ var (
)
type Config struct {
ChainConfig *core.ChainConfig // chain configuration
ChainConfig *params.ChainConfig // chain configuration
NetworkId int // Network ID to use for selecting peers to connect to
Genesis string // Genesis JSON to seed the chain database with
@@ -104,14 +104,13 @@ type Config struct {
}
type Ethereum struct {
chainConfig *core.ChainConfig
chainConfig *params.ChainConfig
// Channel for shutting down the ethereum
shutdownChan chan bool
// DB interfaces
chainDb ethdb.Database // Block chain database
dappDb ethdb.Database // Dapp database
// Handlers
txPool *core.TxPool
txMu sync.Mutex
@@ -253,10 +252,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig)
eth.chainConfig = config.ChainConfig
eth.chainConfig.VmConfig = vm.Config{
EnableJit: config.EnableJit,
ForceJit: config.ForceJit,
}
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux())
if err != nil {

View File

@@ -25,6 +25,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
func TestMipmapUpgrade(t *testing.T) {
@@ -32,7 +33,7 @@ func TestMipmapUpgrade(t *testing.T) {
addr := common.BytesToAddress([]byte("jeff"))
genesis := core.WriteGenesisBlockForTesting(db)
chain, receipts := core.GenerateChain(nil, genesis, db, 10, func(i int, gen *core.BlockGen) {
chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {
var receipts types.Receipts
switch i {
case 1:

View File

@@ -48,23 +48,17 @@ var (
MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request
MaxStateFetch = 384 // Amount of node state values to allow fetching per request
MaxForkAncestry = 3 * params.EpochDuration.Uint64() // Maximum chain reorganisation
hashTTL = 3 * time.Second // [eth/61] Time it takes for a hash request to time out
blockTargetRTT = 3 * time.Second / 2 // [eth/61] Target time for completing a block retrieval request
blockTTL = 3 * blockTargetRTT // [eth/61] Maximum time allowance before a block request is considered expired
rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests
rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion
ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts
MaxForkAncestry = 3 * params.EpochDuration.Uint64() // Maximum chain reorganisation
rttMinEstimate = 2 * time.Second // Minimum round-trip time to target for download requests
rttMaxEstimate = 20 * time.Second // Maximum rount-trip time to target for download requests
rttMinConfidence = 0.1 // Worse confidence factor in our estimated RTT value
ttlScaling = 3 // Constant scaling factor for RTT -> TTL conversion
ttlLimit = time.Minute // Maximum TTL allowance to prevent reaching crazy timeouts
qosTuningPeers = 5 // Number of peers to tune based on (best peers)
qosConfidenceCap = 10 // Number of peers above which not to modify RTT confidence
qosTuningImpact = 0.25 // Impact that a new tuning target has on the previous value
maxQueuedHashes = 32 * 1024 // [eth/61] Maximum number of hashes to queue for import (DOS protection)
maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection)
maxHeadersProcess = 2048 // Number of header download results to import at once into the chain
maxResultsProcess = 2048 // Number of content download results to import at once into the chain
@@ -84,16 +78,13 @@ var (
errStallingPeer = errors.New("peer is stalling")
errNoPeers = errors.New("no peers to keep download active")
errTimeout = errors.New("timeout")
errEmptyHashSet = errors.New("empty hash set by peer")
errEmptyHeaderSet = errors.New("empty header set by peer")
errPeersUnavailable = errors.New("no peers available or all tried for download")
errAlreadyInPool = errors.New("hash already in pool")
errInvalidAncestor = errors.New("retrieved ancestor is invalid")
errInvalidChain = errors.New("retrieved hash chain is invalid")
errInvalidBlock = errors.New("retrieved block is invalid")
errInvalidBody = errors.New("retrieved block body is invalid")
errInvalidReceipt = errors.New("retrieved receipt is invalid")
errCancelHashFetch = errors.New("hash download canceled (requested)")
errCancelBlockFetch = errors.New("block download canceled (requested)")
errCancelHeaderFetch = errors.New("block header download canceled (requested)")
errCancelBodyFetch = errors.New("block body download canceled (requested)")
@@ -102,6 +93,7 @@ var (
errCancelHeaderProcessing = errors.New("header processing canceled (requested)")
errCancelContentProcessing = errors.New("content processing canceled (requested)")
errNoSyncActive = errors.New("no sync active")
errTooOld = errors.New("peer doesn't speak recent enough protocol version (need version >= 62)")
)
type Downloader struct {
@@ -146,20 +138,19 @@ type Downloader struct {
// Channels
newPeerCh chan *peer
hashCh chan dataPack // [eth/61] Channel receiving inbound hashes
blockCh chan dataPack // [eth/61] Channel receiving inbound blocks
headerCh chan dataPack // [eth/62] Channel receiving inbound block headers
bodyCh chan dataPack // [eth/62] Channel receiving inbound block bodies
receiptCh chan dataPack // [eth/63] Channel receiving inbound receipts
stateCh chan dataPack // [eth/63] Channel receiving inbound node state data
blockWakeCh chan bool // [eth/61] Channel to signal the block fetcher of new tasks
bodyWakeCh chan bool // [eth/62] Channel to signal the block body fetcher of new tasks
receiptWakeCh chan bool // [eth/63] Channel to signal the receipt fetcher of new tasks
stateWakeCh chan bool // [eth/63] Channel to signal the state fetcher of new tasks
headerProcCh chan []*types.Header // [eth/62] Channel to feed the header processor new tasks
// Cancellation and termination
cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop)
cancelCh chan struct{} // Channel to cancel mid-flight syncs
cancelLock sync.RWMutex // Lock to protect the cancel channel in delivers
cancelLock sync.RWMutex // Lock to protect the cancel channel and peer in delivers
quitCh chan struct{} // Quit channel to signal termination
quitLock sync.RWMutex // Lock to prevent double closes
@@ -199,13 +190,10 @@ func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, ha
rollback: rollback,
dropPeer: dropPeer,
newPeerCh: make(chan *peer, 1),
hashCh: make(chan dataPack, 1),
blockCh: make(chan dataPack, 1),
headerCh: make(chan dataPack, 1),
bodyCh: make(chan dataPack, 1),
receiptCh: make(chan dataPack, 1),
stateCh: make(chan dataPack, 1),
blockWakeCh: make(chan bool, 1),
bodyWakeCh: make(chan bool, 1),
receiptWakeCh: make(chan bool, 1),
stateWakeCh: make(chan bool, 1),
@@ -250,13 +238,12 @@ func (d *Downloader) Synchronising() bool {
// RegisterPeer injects a new download peer into the set of block source to be
// used for fetching hashes and blocks from.
func (d *Downloader) RegisterPeer(id string, version int, head common.Hash,
getRelHashes relativeHashFetcherFn, getAbsHashes absoluteHashFetcherFn, getBlocks blockFetcherFn, // eth/61 callbacks, remove when upgrading
func (d *Downloader) RegisterPeer(id string, version int, currentHead currentHeadRetrievalFn,
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) error {
glog.V(logger.Detail).Infoln("Registering peer", id)
if err := d.peers.Register(newPeer(id, version, head, getRelHashes, getAbsHashes, getBlocks, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil {
if err := d.peers.Register(newPeer(id, version, currentHead, getRelHeaders, getAbsHeaders, getBlockBodies, getReceipts, getNodeData)); err != nil {
glog.V(logger.Error).Infoln("Register failed:", err)
return err
}
@@ -269,12 +256,22 @@ func (d *Downloader) RegisterPeer(id string, version int, head common.Hash,
// the specified peer. An effort is also made to return any pending fetches into
// the queue.
func (d *Downloader) UnregisterPeer(id string) error {
// Unregister the peer from the active peer set and revoke any fetch tasks
glog.V(logger.Detail).Infoln("Unregistering peer", id)
if err := d.peers.Unregister(id); err != nil {
glog.V(logger.Error).Infoln("Unregister failed:", err)
return err
}
d.queue.Revoke(id)
// If this peer was the master peer, abort sync immediately
d.cancelLock.RLock()
master := id == d.cancelPeer
d.cancelLock.RUnlock()
if master {
d.cancel()
}
return nil
}
@@ -291,7 +288,9 @@ func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode
case errBusy:
glog.V(logger.Detail).Infof("Synchronisation already in progress")
case errTimeout, errBadPeer, errStallingPeer, errEmptyHashSet, errEmptyHeaderSet, errPeersUnavailable, errInvalidAncestor, errInvalidChain:
case errTimeout, errBadPeer, errStallingPeer,
errEmptyHeaderSet, errPeersUnavailable, errTooOld,
errInvalidAncestor, errInvalidChain:
glog.V(logger.Debug).Infof("Removing peer %v: %v", id, err)
d.dropPeer(id)
@@ -323,13 +322,13 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
d.queue.Reset()
d.peers.Reset()
for _, ch := range []chan bool{d.blockWakeCh, d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} {
for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh, d.stateWakeCh} {
select {
case <-ch:
default:
}
}
for _, ch := range []chan dataPack{d.hashCh, d.blockCh, d.headerCh, d.bodyCh, d.receiptCh, d.stateCh} {
for _, ch := range []chan dataPack{d.headerCh, d.bodyCh, d.receiptCh, d.stateCh} {
for empty := false; !empty; {
select {
case <-ch:
@@ -345,9 +344,10 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode
empty = true
}
}
// Create cancel channel for aborting mid-flight
// Create cancel channel for aborting mid-flight and mark the master peer
d.cancelLock.Lock()
d.cancelCh = make(chan struct{})
d.cancelPeer = id
d.cancelLock.Unlock()
defer d.cancel() // No matter what, we can't leave the cancel channel open
@@ -377,105 +377,73 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
d.mux.Post(DoneEvent{})
}
}()
if p.version < 62 {
return errTooOld
}
glog.V(logger.Debug).Infof("Synchronising with the network using: %s [eth/%d]", p.id, p.version)
defer func(start time.Time) {
glog.V(logger.Debug).Infof("Synchronisation terminated after %v", time.Since(start))
}(time.Now())
switch {
case p.version == 61:
// Look up the sync boundaries: the common ancestor and the target block
latest, err := d.fetchHeight61(p)
if err != nil {
return err
}
origin, err := d.findAncestor61(p, latest)
if err != nil {
return err
}
d.syncStatsLock.Lock()
if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
d.syncStatsChainOrigin = origin
}
d.syncStatsChainHeight = latest
d.syncStatsLock.Unlock()
// Initiate the sync using a concurrent hash and block retrieval algorithm
d.queue.Prepare(origin+1, d.mode, 0, nil)
if d.syncInitHook != nil {
d.syncInitHook(origin, latest)
}
return d.spawnSync(origin+1,
func() error { return d.fetchHashes61(p, td, origin+1) },
func() error { return d.fetchBlocks61(origin + 1) },
)
case p.version >= 62:
// Look up the sync boundaries: the common ancestor and the target block
latest, err := d.fetchHeight(p)
if err != nil {
return err
}
height := latest.Number.Uint64()
origin, err := d.findAncestor(p, height)
if err != nil {
return err
}
d.syncStatsLock.Lock()
if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
d.syncStatsChainOrigin = origin
}
d.syncStatsChainHeight = height
d.syncStatsLock.Unlock()
// Initiate the sync using a concurrent header and content retrieval algorithm
pivot := uint64(0)
switch d.mode {
case LightSync:
pivot = height
case FastSync:
// Calculate the new fast/slow sync pivot point
if d.fsPivotLock == nil {
pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval)))
if err != nil {
panic(fmt.Sprintf("Failed to access crypto random source: %v", err))
}
if height > uint64(fsMinFullBlocks)+pivotOffset.Uint64() {
pivot = height - uint64(fsMinFullBlocks) - pivotOffset.Uint64()
}
} else {
// Pivot point locked in, use this and do not pick a new one!
pivot = d.fsPivotLock.Number.Uint64()
}
// If the point is below the origin, move origin back to ensure state download
if pivot < origin {
if pivot > 0 {
origin = pivot - 1
} else {
origin = 0
}
}
glog.V(logger.Debug).Infof("Fast syncing until pivot block #%d", pivot)
}
d.queue.Prepare(origin+1, d.mode, pivot, latest)
if d.syncInitHook != nil {
d.syncInitHook(origin, height)
}
return d.spawnSync(origin+1,
func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved
func() error { return d.processHeaders(origin+1, td) }, // Headers are always retrieved
func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
func() error { return d.fetchNodeData() }, // Node state data is retrieved during fast sync
)
default:
// Something very wrong, stop right here
glog.V(logger.Error).Infof("Unsupported eth protocol: %d", p.version)
return errBadPeer
// Look up the sync boundaries: the common ancestor and the target block
latest, err := d.fetchHeight(p)
if err != nil {
return err
}
height := latest.Number.Uint64()
origin, err := d.findAncestor(p, height)
if err != nil {
return err
}
d.syncStatsLock.Lock()
if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
d.syncStatsChainOrigin = origin
}
d.syncStatsChainHeight = height
d.syncStatsLock.Unlock()
// Initiate the sync using a concurrent header and content retrieval algorithm
pivot := uint64(0)
switch d.mode {
case LightSync:
pivot = height
case FastSync:
// Calculate the new fast/slow sync pivot point
if d.fsPivotLock == nil {
pivotOffset, err := rand.Int(rand.Reader, big.NewInt(int64(fsPivotInterval)))
if err != nil {
panic(fmt.Sprintf("Failed to access crypto random source: %v", err))
}
if height > uint64(fsMinFullBlocks)+pivotOffset.Uint64() {
pivot = height - uint64(fsMinFullBlocks) - pivotOffset.Uint64()
}
} else {
// Pivot point locked in, use this and do not pick a new one!
pivot = d.fsPivotLock.Number.Uint64()
}
// If the point is below the origin, move origin back to ensure state download
if pivot < origin {
if pivot > 0 {
origin = pivot - 1
} else {
origin = 0
}
}
glog.V(logger.Debug).Infof("Fast syncing until pivot block #%d", pivot)
}
d.queue.Prepare(origin+1, d.mode, pivot, latest)
if d.syncInitHook != nil {
d.syncInitHook(origin, height)
}
return d.spawnSync(origin+1,
func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved
func() error { return d.processHeaders(origin+1, td) }, // Headers are always retrieved
func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
func() error { return d.fetchNodeData() }, // Node state data is retrieved during fast sync
)
}
// spawnSync runs d.process and all given fetcher functions to completion in
@@ -540,459 +508,14 @@ func (d *Downloader) Terminate() {
d.cancel()
}
// fetchHeight61 retrieves the head block of the remote peer to aid in estimating
// the total time a pending synchronisation would take.
func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
// Request the advertised remote head block and wait for the response
go p.getBlocks([]common.Hash{p.head})
timeout := time.After(hashTTL)
for {
select {
case <-d.cancelCh:
return 0, errCancelBlockFetch
case packet := <-d.blockCh:
// Discard anything not from the origin peer
if packet.PeerId() != p.id {
glog.V(logger.Debug).Infof("Received blocks from incorrect peer(%s)", packet.PeerId())
break
}
// Make sure the peer actually gave something valid
blocks := packet.(*blockPack).blocks
if len(blocks) != 1 {
glog.V(logger.Debug).Infof("%v: invalid number of head blocks: %d != 1", p, len(blocks))
return 0, errBadPeer
}
return blocks[0].NumberU64(), nil
case <-timeout:
glog.V(logger.Debug).Infof("%v: head block timeout", p)
return 0, errTimeout
case <-d.hashCh:
// Out of bounds hashes received, ignore them
case <-d.headerCh:
case <-d.bodyCh:
case <-d.stateCh:
case <-d.receiptCh:
// Ignore eth/{62,63} packets because this is eth/61.
// These can arrive as a late delivery from a previous sync.
}
}
}
// findAncestor61 tries to locate the common ancestor block of the local chain and
// a remote peers blockchain. In the general case when our node was in sync and
// on the correct chain, checking the top N blocks should already get us a match.
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
// the head blocks match), we do a binary search to find the common ancestor.
func (d *Downloader) findAncestor61(p *peer, height uint64) (uint64, error) {
glog.V(logger.Debug).Infof("%v: looking for common ancestor", p)
// Figure out the valid ancestor range to prevent rewrite attacks
floor, ceil := int64(-1), d.headBlock().NumberU64()
if ceil >= MaxForkAncestry {
floor = int64(ceil - MaxForkAncestry)
}
// Request the topmost blocks to short circuit binary ancestor lookup
head := ceil
if head > height {
head = height
}
from := int64(head) - int64(MaxHashFetch) + 1
if from < 0 {
from = 0
}
go p.getAbsHashes(uint64(from), MaxHashFetch)
// Wait for the remote response to the head fetch
number, hash := uint64(0), common.Hash{}
timeout := time.After(hashTTL)
for finished := false; !finished; {
select {
case <-d.cancelCh:
return 0, errCancelHashFetch
case packet := <-d.hashCh:
// Discard anything not from the origin peer
if packet.PeerId() != p.id {
glog.V(logger.Debug).Infof("Received hashes from incorrect peer(%s)", packet.PeerId())
break
}
// Make sure the peer actually gave something valid
hashes := packet.(*hashPack).hashes
if len(hashes) == 0 {
glog.V(logger.Debug).Infof("%v: empty head hash set", p)
return 0, errEmptyHashSet
}
// Check if a common ancestor was found
finished = true
for i := len(hashes) - 1; i >= 0; i-- {
// Skip any headers that underflow/overflow our requested set
header := d.getHeader(hashes[i])
if header == nil || header.Number.Int64() < from || header.Number.Uint64() > head {
continue
}
// Otherwise check if we already know the header or not
if d.hasBlockAndState(hashes[i]) {
number, hash = header.Number.Uint64(), header.Hash()
break
}
}
case <-timeout:
glog.V(logger.Debug).Infof("%v: head hash timeout", p)
return 0, errTimeout
case <-d.blockCh:
// Out of bounds blocks received, ignore them
case <-d.headerCh:
case <-d.bodyCh:
case <-d.stateCh:
case <-d.receiptCh:
// Ignore eth/{62,63} packets because this is eth/61.
// These can arrive as a late delivery from a previous sync.
}
}
// If the head fetch already found an ancestor, return
if !common.EmptyHash(hash) {
if int64(number) <= floor {
glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, number, hash[:4], floor)
return 0, errInvalidAncestor
}
glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, number, hash[:4])
return number, nil
}
// Ancestor not found, we need to binary search over our chain
start, end := uint64(0), head
if floor > 0 {
start = uint64(floor)
}
for start+1 < end {
// Split our chain interval in two, and request the hash to cross check
check := (start + end) / 2
timeout := time.After(hashTTL)
go p.getAbsHashes(uint64(check), 1)
// Wait until a reply arrives to this request
for arrived := false; !arrived; {
select {
case <-d.cancelCh:
return 0, errCancelHashFetch
case packet := <-d.hashCh:
// Discard anything not from the origin peer
if packet.PeerId() != p.id {
glog.V(logger.Debug).Infof("Received hashes from incorrect peer(%s)", packet.PeerId())
break
}
// Make sure the peer actually gave something valid
hashes := packet.(*hashPack).hashes
if len(hashes) != 1 {
glog.V(logger.Debug).Infof("%v: invalid search hash set (%d)", p, len(hashes))
return 0, errBadPeer
}
arrived = true
// Modify the search interval based on the response
if !d.hasBlockAndState(hashes[0]) {
end = check
break
}
block := d.getBlock(hashes[0]) // this doesn't check state, hence the above explicit check
if block.NumberU64() != check {
glog.V(logger.Debug).Infof("%v: non requested hash #%d [%x…], instead of #%d", p, block.NumberU64(), block.Hash().Bytes()[:4], check)
return 0, errBadPeer
}
start = check
case <-timeout:
glog.V(logger.Debug).Infof("%v: search hash timeout", p)
return 0, errTimeout
case <-d.blockCh:
// Out of bounds blocks received, ignore them
case <-d.headerCh:
case <-d.bodyCh:
case <-d.stateCh:
case <-d.receiptCh:
// Ignore eth/{62,63} packets because this is eth/61.
// These can arrive as a late delivery from a previous sync.
}
}
}
// Ensure valid ancestry and return
if int64(start) <= floor {
glog.V(logger.Warn).Infof("%v: potential rewrite attack: #%d [%x…] <= #%d limit", p, start, hash[:4], floor)
return 0, errInvalidAncestor
}
glog.V(logger.Debug).Infof("%v: common ancestor: #%d [%x…]", p, start, hash[:4])
return start, nil
}
// fetchHashes61 keeps retrieving hashes from the requested number, until no more
// are returned, potentially throttling on the way.
func (d *Downloader) fetchHashes61(p *peer, td *big.Int, from uint64) error {
glog.V(logger.Debug).Infof("%v: downloading hashes from #%d", p, from)
// Create a timeout timer, and the associated hash fetcher
request := time.Now() // time of the last fetch request
timeout := time.NewTimer(0) // timer to dump a non-responsive active peer
<-timeout.C // timeout channel should be initially empty
defer timeout.Stop()
getHashes := func(from uint64) {
glog.V(logger.Detail).Infof("%v: fetching %d hashes from #%d", p, MaxHashFetch, from)
request = time.Now()
timeout.Reset(hashTTL)
go p.getAbsHashes(from, MaxHashFetch)
}
// Start pulling hashes, until all are exhausted
getHashes(from)
gotHashes := false
for {
select {
case <-d.cancelCh:
return errCancelHashFetch
case packet := <-d.hashCh:
// Make sure the active peer is giving us the hashes
if packet.PeerId() != p.id {
glog.V(logger.Debug).Infof("Received hashes from incorrect peer(%s)", packet.PeerId())
break
}
hashReqTimer.UpdateSince(request)
timeout.Stop()
// If no more hashes are inbound, notify the block fetcher and return
if packet.Items() == 0 {
glog.V(logger.Debug).Infof("%v: no available hashes", p)
select {
case d.blockWakeCh <- false:
case <-d.cancelCh:
}
// If no hashes were retrieved at all, the peer violated it's TD promise that it had a
// better chain compared to ours. The only exception is if it's promised blocks were
// already imported by other means (e.g. fetcher):
//
// R <remote peer>, L <local node>: Both at block 10
// R: Mine block 11, and propagate it to L
// L: Queue block 11 for import
// L: Notice that R's head and TD increased compared to ours, start sync
// L: Import of block 11 finishes
// L: Sync begins, and finds common ancestor at 11
// L: Request new hashes up from 11 (R's TD was higher, it must have something)
// R: Nothing to give
if !gotHashes && td.Cmp(d.getTd(d.headBlock().Hash())) > 0 {
return errStallingPeer
}
return nil
}
gotHashes = true
hashes := packet.(*hashPack).hashes
// Otherwise insert all the new hashes, aborting in case of junk
glog.V(logger.Detail).Infof("%v: scheduling %d hashes from #%d", p, len(hashes), from)
inserts := d.queue.Schedule61(hashes, true)
if len(inserts) != len(hashes) {
glog.V(logger.Debug).Infof("%v: stale hashes", p)
return errBadPeer
}
// Notify the block fetcher of new hashes, but stop if queue is full
if d.queue.PendingBlocks() < maxQueuedHashes {
// We still have hashes to fetch, send continuation wake signal (potential)
select {
case d.blockWakeCh <- true:
default:
}
} else {
// Hash limit reached, send a termination wake signal (enforced)
select {
case d.blockWakeCh <- false:
case <-d.cancelCh:
}
return nil
}
// Queue not yet full, fetch the next batch
from += uint64(len(hashes))
getHashes(from)
case <-timeout.C:
glog.V(logger.Debug).Infof("%v: hash request timed out", p)
hashTimeoutMeter.Mark(1)
return errTimeout
case <-d.headerCh:
case <-d.bodyCh:
case <-d.stateCh:
case <-d.receiptCh:
// Ignore eth/{62,63} packets because this is eth/61.
// These can arrive as a late delivery from a previous sync.
}
}
}
// fetchBlocks61 iteratively downloads the scheduled hashes, taking any available
// peers, reserving a chunk of blocks for each, waiting for delivery and also
// periodically checking for timeouts.
func (d *Downloader) fetchBlocks61(from uint64) error {
glog.V(logger.Debug).Infof("Downloading blocks from #%d", from)
defer glog.V(logger.Debug).Infof("Block download terminated")
// Create a timeout timer for scheduling expiration tasks
ticker := time.NewTicker(100 * time.Millisecond)
defer ticker.Stop()
update := make(chan struct{}, 1)
// Fetch blocks until the hash fetcher's done
finished := false
for {
select {
case <-d.cancelCh:
return errCancelBlockFetch
case packet := <-d.blockCh:
// If the peer was previously banned and failed to deliver it's pack
// in a reasonable time frame, ignore it's message.
if peer := d.peers.Peer(packet.PeerId()); peer != nil {
blocks := packet.(*blockPack).blocks
// Deliver the received chunk of blocks and check chain validity
accepted, err := d.queue.DeliverBlocks(peer.id, blocks)
if err == errInvalidChain {
return err
}
// Unless a peer delivered something completely else than requested (usually
// caused by a timed out request which came through in the end), set it to
// idle. If the delivery's stale, the peer should have already been idled.
if err != errStaleDelivery {
peer.SetBlocksIdle(accepted)
}
// Issue a log to the user to see what's going on
switch {
case err == nil && len(blocks) == 0:
glog.V(logger.Detail).Infof("%s: no blocks delivered", peer)
case err == nil:
glog.V(logger.Detail).Infof("%s: delivered %d blocks", peer, len(blocks))
default:
glog.V(logger.Detail).Infof("%s: delivery failed: %v", peer, err)
}
}
// Blocks arrived, try to update the progress
select {
case update <- struct{}{}:
default:
}
case cont := <-d.blockWakeCh:
// The hash fetcher sent a continuation flag, check if it's done
if !cont {
finished = true
}
// Hashes arrive, try to update the progress
select {
case update <- struct{}{}:
default:
}
case <-ticker.C:
// Sanity check update the progress
select {
case update <- struct{}{}:
default:
}
case <-update:
// Short circuit if we lost all our peers
if d.peers.Len() == 0 {
return errNoPeers
}
// Check for block request timeouts and demote the responsible peers
for pid, fails := range d.queue.ExpireBlocks(blockTTL) {
if peer := d.peers.Peer(pid); peer != nil {
if fails > 1 {
glog.V(logger.Detail).Infof("%s: block delivery timeout", peer)
peer.SetBlocksIdle(0)
} else {
glog.V(logger.Debug).Infof("%s: stalling block delivery, dropping", peer)
d.dropPeer(pid)
}
}
}
// If there's nothing more to fetch, wait or terminate
if d.queue.PendingBlocks() == 0 {
if !d.queue.InFlightBlocks() && finished {
glog.V(logger.Debug).Infof("Block fetching completed")
return nil
}
break
}
// Send a download request to all idle peers, until throttled
throttled := false
idles, total := d.peers.BlockIdlePeers()
for _, peer := range idles {
// Short circuit if throttling activated
if d.queue.ShouldThrottleBlocks() {
throttled = true
break
}
// Reserve a chunk of hashes for a peer. A nil can mean either that
// no more hashes are available, or that the peer is known not to
// have them.
request := d.queue.ReserveBlocks(peer, peer.BlockCapacity(blockTargetRTT))
if request == nil {
continue
}
if glog.V(logger.Detail) {
glog.Infof("%s: requesting %d blocks", peer, len(request.Hashes))
}
// Fetch the chunk and make sure any errors return the hashes to the queue
if err := peer.Fetch61(request); err != nil {
// Although we could try and make an attempt to fix this, this error really
// means that we've double allocated a fetch task to a peer. If that is the
// case, the internal state of the downloader and the queue is very wrong so
// better hard crash and note the error instead of silently accumulating into
// a much bigger issue.
panic(fmt.Sprintf("%v: fetch assignment failed", peer))
}
}
// Make sure that we have peers available for fetching. If all peers have been tried
// and all failed throw an error
if !throttled && !d.queue.InFlightBlocks() && len(idles) == total {
return errPeersUnavailable
}
case <-d.headerCh:
case <-d.bodyCh:
case <-d.stateCh:
case <-d.receiptCh:
// Ignore eth/{62,63} packets because this is eth/61.
// These can arrive as a late delivery from a previous sync.
}
}
}
// fetchHeight retrieves the head header of the remote peer to aid in estimating
// the total time a pending synchronisation would take.
func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
// Request the advertised remote head block and wait for the response
go p.getRelHeaders(p.head, 1, 0, false)
head, _ := p.currentHead()
go p.getRelHeaders(head, 1, 0, false)
timeout := time.After(d.requestTTL())
for {
@@ -1022,11 +545,6 @@ func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
case <-d.stateCh:
case <-d.receiptCh:
// Out of bounds delivery, ignore
case <-d.hashCh:
case <-d.blockCh:
// Ignore eth/61 packets because this is eth/62+.
// These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1037,7 +555,7 @@ func (d *Downloader) fetchHeight(p *peer) (*types.Header, error) {
// In the rare scenario when we ended up on a long reorganisation (i.e. none of
// the head links match), we do a binary search to find the common ancestor.
func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
glog.V(logger.Debug).Infof("%v: looking for common ancestor", p)
glog.V(logger.Debug).Infof("%v: looking for common ancestor (remote height %d)", p, height)
// Figure out the valid ancestor range to prevent rewrite attacks
floor, ceil := int64(-1), d.headHeader().Number.Uint64()
@@ -1054,11 +572,17 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
if head > height {
head = height
}
from := int64(head) - int64(MaxHeaderFetch) + 1
from := int64(head) - int64(MaxHeaderFetch)
if from < 0 {
from = 0
}
go p.getAbsHeaders(uint64(from), MaxHeaderFetch, 0, false)
// Span out with 15 block gaps into the future to catch bad head reports
limit := 2 * MaxHeaderFetch / 16
count := 1 + int((int64(ceil)-from)/16)
if count > limit {
count = limit
}
go p.getAbsHeaders(uint64(from), count, 15, false)
// Wait for the remote response to the head fetch
number, hash := uint64(0), common.Hash{}
@@ -1067,7 +591,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
for finished := false; !finished; {
select {
case <-d.cancelCh:
return 0, errCancelHashFetch
return 0, errCancelHeaderFetch
case packet := <-d.headerCh:
// Discard anything not from the origin peer
@@ -1083,12 +607,8 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
}
// Make sure the peer's reply conforms to the request
for i := 0; i < len(headers); i++ {
if number := headers[i].Number.Int64(); number != from+int64(i) {
glog.V(logger.Warn).Infof("%v: head header set (item %d) broke chain ordering: requested %d, got %d", p, i, from+int64(i), number)
return 0, errInvalidChain
}
if i > 0 && headers[i-1].Hash() != headers[i].ParentHash {
glog.V(logger.Warn).Infof("%v: head header set (item %d) broke chain ancestry: expected [%x], got [%x]", p, i, headers[i-1].Hash().Bytes()[:4], headers[i].ParentHash[:4])
if number := headers[i].Number.Int64(); number != from+int64(i)*16 {
glog.V(logger.Warn).Infof("%v: head header set (item %d) broke chain ordering: requested %d, got %d", p, i, from+int64(i)*16, number)
return 0, errInvalidChain
}
}
@@ -1096,12 +616,18 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
finished = true
for i := len(headers) - 1; i >= 0; i-- {
// Skip any headers that underflow/overflow our requested set
if headers[i].Number.Int64() < from || headers[i].Number.Uint64() > head {
if headers[i].Number.Int64() < from || headers[i].Number.Uint64() > ceil {
continue
}
// Otherwise check if we already know the header or not
if (d.mode == FullSync && d.hasBlockAndState(headers[i].Hash())) || (d.mode != FullSync && d.hasHeader(headers[i].Hash())) {
number, hash = headers[i].Number.Uint64(), headers[i].Hash()
// If every header is known, even future ones, the peer straight out lied about its head
if number > height && i == limit-1 {
glog.V(logger.Warn).Infof("%v: lied about chain head: reported %d, found above %d", p, height, number)
return 0, errStallingPeer
}
break
}
}
@@ -1114,11 +640,6 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
case <-d.stateCh:
case <-d.receiptCh:
// Out of bounds delivery, ignore
case <-d.hashCh:
case <-d.blockCh:
// Ignore eth/61 packets because this is eth/62+.
// These can arrive as a late delivery from a previous sync.
}
}
// If the head fetch already found an ancestor, return
@@ -1146,7 +667,7 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
for arrived := false; !arrived; {
select {
case <-d.cancelCh:
return 0, errCancelHashFetch
return 0, errCancelHeaderFetch
case packer := <-d.headerCh:
// Discard anything not from the origin peer
@@ -1182,11 +703,6 @@ func (d *Downloader) findAncestor(p *peer, height uint64) (uint64, error) {
case <-d.stateCh:
case <-d.receiptCh:
// Out of bounds delivery, ignore
case <-d.hashCh:
case <-d.blockCh:
// Ignore eth/61 packets because this is eth/62+.
// These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1305,11 +821,6 @@ func (d *Downloader) fetchHeaders(p *peer, from uint64) error {
case <-d.cancelCh:
}
return errBadPeer
case <-d.hashCh:
case <-d.blockCh:
// Ignore eth/61 packets because this is eth/62+.
// These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1555,7 +1066,14 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
// Check for fetch request timeouts and demote the responsible peers
for pid, fails := range expire() {
if peer := d.peers.Peer(pid); peer != nil {
if fails > 1 {
// If a lot of retrieval elements expired, we might have overestimated the remote peer or perhaps
// ourselves. Only reset to minimal throughput but don't drop just yet. If even the minimal times
// out that sync wise we need to get rid of the peer.
//
// The reason the minimum threshold is 2 is because the downloader tries to estimate the bandwidth
// and latency of a peer separately, which requires pushing the measures capacity a bit and seeing
// how response times reacts, to it always requests one more than the minimum (i.e. min 2).
if fails > 2 {
glog.V(logger.Detail).Infof("%s: %s delivery timeout", peer, strings.ToLower(kind))
setIdle(peer, 0)
} else {
@@ -1623,11 +1141,6 @@ func (d *Downloader) fetchParts(errCancel error, deliveryCh chan dataPack, deliv
if !progressed && !throttled && !running && len(idles) == total && pending() > 0 {
return errPeersUnavailable
}
case <-d.hashCh:
case <-d.blockCh:
// Ignore eth/61 packets because this is eth/62+.
// These can arrive as a late delivery from a previous sync.
}
}
}
@@ -1867,19 +1380,6 @@ func (d *Downloader) processContent() error {
}
}
// DeliverHashes injects a new batch of hashes received from a remote node into
// the download schedule. This is usually invoked through the BlockHashesMsg by
// the protocol handler.
func (d *Downloader) DeliverHashes(id string, hashes []common.Hash) (err error) {
return d.deliver(id, d.hashCh, &hashPack{id, hashes}, hashInMeter, hashDropMeter)
}
// DeliverBlocks injects a new batch of blocks received from a remote node.
// This is usually invoked through the BlocksMsg by the protocol handler.
func (d *Downloader) DeliverBlocks(id string, blocks []*types.Block) (err error) {
return d.deliver(id, d.blockCh, &blockPack{id, blocks}, blockInMeter, blockDropMeter)
}
// DeliverHeaders injects a new batch of block headers received from a remote
// node into the download schedule.
func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) (err error) {

View File

@@ -55,7 +55,7 @@ func init() {
// reassembly.
func makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts) {
// Generate the block chain
blocks, receipts := core.GenerateChain(nil, parent, testdb, n, func(i int, block *core.BlockGen) {
blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, testdb, n, func(i int, block *core.BlockGen) {
block.SetCoinbase(common.Address{seed})
// If a heavy chain is requested, delay blocks to raise difficulty
@@ -286,7 +286,7 @@ func (dl *downloadTester) headFastBlock() *types.Block {
func (dl *downloadTester) commitHeadBlock(hash common.Hash) error {
// For now only check that the state trie is correct
if block := dl.getBlock(hash); block != nil {
_, err := trie.NewSecure(block.Root(), dl.stateDb)
_, err := trie.NewSecure(block.Root(), dl.stateDb, 0)
return err
}
return fmt.Errorf("non existent block: %x", hash[:4])
@@ -399,14 +399,12 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha
var err error
switch version {
case 61:
err = dl.downloader.RegisterPeer(id, version, hashes[0], dl.peerGetRelHashesFn(id, delay), dl.peerGetAbsHashesFn(id, delay), dl.peerGetBlocksFn(id, delay), nil, nil, nil, nil, nil)
case 62:
err = dl.downloader.RegisterPeer(id, version, hashes[0], nil, nil, nil, dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), nil, nil)
err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), nil, nil)
case 63:
err = dl.downloader.RegisterPeer(id, version, hashes[0], nil, nil, nil, dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
case 64:
err = dl.downloader.RegisterPeer(id, version, hashes[0], nil, nil, nil, dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
err = dl.downloader.RegisterPeer(id, version, dl.peerCurrentHeadFn(id), dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay), dl.peerGetReceiptsFn(id, delay), dl.peerGetNodeDataFn(id, delay))
}
if err == nil {
// Assign the owned hashes, headers and blocks to the peer (deep copy)
@@ -465,83 +463,14 @@ func (dl *downloadTester) dropPeer(id string) {
dl.downloader.UnregisterPeer(id)
}
// peerGetRelHashesFn constructs a GetHashes function associated with a specific
// peer in the download tester. The returned function can be used to retrieve
// batches of hashes from the particularly requested peer.
func (dl *downloadTester) peerGetRelHashesFn(id string, delay time.Duration) func(head common.Hash) error {
return func(head common.Hash) error {
time.Sleep(delay)
// peerCurrentHeadFn constructs a function to retrieve a peer's current head hash
// and total difficulty.
func (dl *downloadTester) peerCurrentHeadFn(id string) func() (common.Hash, *big.Int) {
return func() (common.Hash, *big.Int) {
dl.lock.RLock()
defer dl.lock.RUnlock()
// Gather the next batch of hashes
hashes := dl.peerHashes[id]
result := make([]common.Hash, 0, MaxHashFetch)
for i, hash := range hashes {
if hash == head {
i++
for len(result) < cap(result) && i < len(hashes) {
result = append(result, hashes[i])
i++
}
break
}
}
// Delay delivery a bit to allow attacks to unfold
go func() {
time.Sleep(time.Millisecond)
dl.downloader.DeliverHashes(id, result)
}()
return nil
}
}
// peerGetAbsHashesFn constructs a GetHashesFromNumber function associated with
// a particular peer in the download tester. The returned function can be used to
// retrieve batches of hashes from the particularly requested peer.
func (dl *downloadTester) peerGetAbsHashesFn(id string, delay time.Duration) func(uint64, int) error {
return func(head uint64, count int) error {
time.Sleep(delay)
dl.lock.RLock()
defer dl.lock.RUnlock()
// Gather the next batch of hashes
hashes := dl.peerHashes[id]
result := make([]common.Hash, 0, count)
for i := 0; i < count && len(hashes)-int(head)-1-i >= 0; i++ {
result = append(result, hashes[len(hashes)-int(head)-1-i])
}
// Delay delivery a bit to allow attacks to unfold
go func() {
time.Sleep(time.Millisecond)
dl.downloader.DeliverHashes(id, result)
}()
return nil
}
}
// peerGetBlocksFn constructs a getBlocks function associated with a particular
// peer in the download tester. The returned function can be used to retrieve
// batches of blocks from the particularly requested peer.
func (dl *downloadTester) peerGetBlocksFn(id string, delay time.Duration) func([]common.Hash) error {
return func(hashes []common.Hash) error {
time.Sleep(delay)
dl.lock.RLock()
defer dl.lock.RUnlock()
blocks := dl.peerBlocks[id]
result := make([]*types.Block, 0, len(hashes))
for _, hash := range hashes {
if block, ok := blocks[hash]; ok {
result = append(result, block)
}
}
go dl.downloader.DeliverBlocks(id, result)
return nil
return dl.peerHashes[id][0], nil
}
}
@@ -730,7 +659,6 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
// Tests that simple synchronization against a canonical chain works correctly.
// In this test common ancestor lookup should be short circuited and not require
// binary searching.
func TestCanonicalSynchronisation61(t *testing.T) { testCanonicalSynchronisation(t, 61, FullSync) }
func TestCanonicalSynchronisation62(t *testing.T) { testCanonicalSynchronisation(t, 62, FullSync) }
func TestCanonicalSynchronisation63Full(t *testing.T) { testCanonicalSynchronisation(t, 63, FullSync) }
func TestCanonicalSynchronisation63Fast(t *testing.T) { testCanonicalSynchronisation(t, 63, FastSync) }
@@ -759,7 +687,6 @@ func testCanonicalSynchronisation(t *testing.T, protocol int, mode SyncMode) {
// Tests that if a large batch of blocks are being downloaded, it is throttled
// until the cached blocks are retrieved.
func TestThrottling61(t *testing.T) { testThrottling(t, 61, FullSync) }
func TestThrottling62(t *testing.T) { testThrottling(t, 62, FullSync) }
func TestThrottling63Full(t *testing.T) { testThrottling(t, 63, FullSync) }
func TestThrottling63Fast(t *testing.T) { testThrottling(t, 63, FastSync) }
@@ -845,7 +772,6 @@ func testThrottling(t *testing.T, protocol int, mode SyncMode) {
// Tests that simple synchronization against a forked chain works correctly. In
// this test common ancestor lookup should *not* be short circuited, and a full
// binary search should be executed.
func TestForkedSync61(t *testing.T) { testForkedSync(t, 61, FullSync) }
func TestForkedSync62(t *testing.T) { testForkedSync(t, 62, FullSync) }
func TestForkedSync63Full(t *testing.T) { testForkedSync(t, 63, FullSync) }
func TestForkedSync63Fast(t *testing.T) { testForkedSync(t, 63, FastSync) }
@@ -881,7 +807,6 @@ func testForkedSync(t *testing.T, protocol int, mode SyncMode) {
// Tests that synchronising against a much shorter but much heavyer fork works
// corrently and is not dropped.
func TestHeavyForkedSync61(t *testing.T) { testHeavyForkedSync(t, 61, FullSync) }
func TestHeavyForkedSync62(t *testing.T) { testHeavyForkedSync(t, 62, FullSync) }
func TestHeavyForkedSync63Full(t *testing.T) { testHeavyForkedSync(t, 63, FullSync) }
func TestHeavyForkedSync63Fast(t *testing.T) { testHeavyForkedSync(t, 63, FastSync) }
@@ -915,24 +840,9 @@ func testHeavyForkedSync(t *testing.T, protocol int, mode SyncMode) {
assertOwnForkedChain(t, tester, common+1, []int{common + fork + 1, common + fork/2 + 1})
}
// Tests that an inactive downloader will not accept incoming hashes and blocks.
func TestInactiveDownloader61(t *testing.T) {
t.Parallel()
tester := newTester()
// Check that neither hashes nor blocks are accepted
if err := tester.downloader.DeliverHashes("bad peer", []common.Hash{}); err != errNoSyncActive {
t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive)
}
if err := tester.downloader.DeliverBlocks("bad peer", []*types.Block{}); err != errNoSyncActive {
t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive)
}
}
// Tests that chain forks are contained within a certain interval of the current
// chain head, ensuring that malicious peers cannot waste resources by feeding
// long dead chains.
func TestBoundedForkedSync61(t *testing.T) { testBoundedForkedSync(t, 61, FullSync) }
func TestBoundedForkedSync62(t *testing.T) { testBoundedForkedSync(t, 62, FullSync) }
func TestBoundedForkedSync63Full(t *testing.T) { testBoundedForkedSync(t, 63, FullSync) }
func TestBoundedForkedSync63Fast(t *testing.T) { testBoundedForkedSync(t, 63, FastSync) }
@@ -968,7 +878,6 @@ func testBoundedForkedSync(t *testing.T, protocol int, mode SyncMode) {
// Tests that chain forks are contained within a certain interval of the current
// chain head for short but heavy forks too. These are a bit special because they
// take different ancestor lookup paths.
func TestBoundedHeavyForkedSync61(t *testing.T) { testBoundedHeavyForkedSync(t, 61, FullSync) }
func TestBoundedHeavyForkedSync62(t *testing.T) { testBoundedHeavyForkedSync(t, 62, FullSync) }
func TestBoundedHeavyForkedSync63Full(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FullSync) }
func TestBoundedHeavyForkedSync63Fast(t *testing.T) { testBoundedHeavyForkedSync(t, 63, FastSync) }
@@ -1039,7 +948,6 @@ func TestInactiveDownloader63(t *testing.T) {
}
// Tests that a canceled download wipes all previously accumulated state.
func TestCancel61(t *testing.T) { testCancel(t, 61, FullSync) }
func TestCancel62(t *testing.T) { testCancel(t, 62, FullSync) }
func TestCancel63Full(t *testing.T) { testCancel(t, 63, FullSync) }
func TestCancel63Fast(t *testing.T) { testCancel(t, 63, FastSync) }
@@ -1081,7 +989,6 @@ func testCancel(t *testing.T, protocol int, mode SyncMode) {
}
// Tests that synchronisation from multiple peers works as intended (multi thread sanity test).
func TestMultiSynchronisation61(t *testing.T) { testMultiSynchronisation(t, 61, FullSync) }
func TestMultiSynchronisation62(t *testing.T) { testMultiSynchronisation(t, 62, FullSync) }
func TestMultiSynchronisation63Full(t *testing.T) { testMultiSynchronisation(t, 63, FullSync) }
func TestMultiSynchronisation63Fast(t *testing.T) { testMultiSynchronisation(t, 63, FastSync) }
@@ -1112,7 +1019,6 @@ func testMultiSynchronisation(t *testing.T, protocol int, mode SyncMode) {
// Tests that synchronisations behave well in multi-version protocol environments
// and not wreak havoc on other nodes in the network.
func TestMultiProtoSynchronisation61(t *testing.T) { testMultiProtoSync(t, 61, FullSync) }
func TestMultiProtoSynchronisation62(t *testing.T) { testMultiProtoSync(t, 62, FullSync) }
func TestMultiProtoSynchronisation63Full(t *testing.T) { testMultiProtoSync(t, 63, FullSync) }
func TestMultiProtoSynchronisation63Fast(t *testing.T) { testMultiProtoSync(t, 63, FastSync) }
@@ -1131,7 +1037,6 @@ func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) {
tester := newTester()
defer tester.terminate()
tester.newPeer("peer 61", 61, hashes, nil, blocks, nil)
tester.newPeer("peer 62", 62, hashes, headers, blocks, nil)
tester.newPeer("peer 63", 63, hashes, headers, blocks, receipts)
tester.newPeer("peer 64", 64, hashes, headers, blocks, receipts)
@@ -1143,7 +1048,7 @@ func testMultiProtoSync(t *testing.T, protocol int, mode SyncMode) {
assertOwnChain(t, tester, targetBlocks+1)
// Check that no peers have been dropped off
for _, version := range []int{61, 62, 63, 64} {
for _, version := range []int{62, 63, 64} {
peer := fmt.Sprintf("peer %d", version)
if _, ok := tester.peerHashes[peer]; !ok {
t.Errorf("%s dropped", peer)
@@ -1368,7 +1273,6 @@ func testInvalidHeaderRollback(t *testing.T, protocol int, mode SyncMode) {
// Tests that a peer advertising an high TD doesn't get to stall the downloader
// afterwards by not sending any useful hashes.
func TestHighTDStarvationAttack61(t *testing.T) { testHighTDStarvationAttack(t, 61, FullSync) }
func TestHighTDStarvationAttack62(t *testing.T) { testHighTDStarvationAttack(t, 62, FullSync) }
func TestHighTDStarvationAttack63Full(t *testing.T) { testHighTDStarvationAttack(t, 63, FullSync) }
func TestHighTDStarvationAttack63Fast(t *testing.T) { testHighTDStarvationAttack(t, 63, FastSync) }
@@ -1391,7 +1295,6 @@ func testHighTDStarvationAttack(t *testing.T, protocol int, mode SyncMode) {
}
// Tests that misbehaving peers are disconnected, whilst behaving ones are not.
func TestBlockHeaderAttackerDropping61(t *testing.T) { testBlockHeaderAttackerDropping(t, 61) }
func TestBlockHeaderAttackerDropping62(t *testing.T) { testBlockHeaderAttackerDropping(t, 62) }
func TestBlockHeaderAttackerDropping63(t *testing.T) { testBlockHeaderAttackerDropping(t, 63) }
func TestBlockHeaderAttackerDropping64(t *testing.T) { testBlockHeaderAttackerDropping(t, 64) }
@@ -1409,7 +1312,6 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
{errStallingPeer, true}, // Peer was detected to be stalling, drop it
{errNoPeers, false}, // No peers to download from, soft race, no issue
{errTimeout, true}, // No hashes received in due time, drop the peer
{errEmptyHashSet, true}, // No hashes were returned as a response, drop as it's a dead end
{errEmptyHeaderSet, true}, // No headers were returned as a response, drop as it's a dead end
{errPeersUnavailable, true}, // Nobody had the advertised blocks, drop the advertiser
{errInvalidAncestor, true}, // Agreed upon ancestor is not acceptable, drop the chain rewriter
@@ -1417,7 +1319,6 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
{errInvalidBlock, false}, // A bad peer was detected, but not the sync origin
{errInvalidBody, false}, // A bad peer was detected, but not the sync origin
{errInvalidReceipt, false}, // A bad peer was detected, but not the sync origin
{errCancelHashFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
{errCancelBlockFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
{errCancelHeaderFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
{errCancelBodyFetch, false}, // Synchronisation was canceled, origin may be innocent, don't drop
@@ -1450,7 +1351,6 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol int) {
// Tests that synchronisation progress (origin block number, current block number
// and highest block number) is tracked and updated correctly.
func TestSyncProgress61(t *testing.T) { testSyncProgress(t, 61, FullSync) }
func TestSyncProgress62(t *testing.T) { testSyncProgress(t, 62, FullSync) }
func TestSyncProgress63Full(t *testing.T) { testSyncProgress(t, 63, FullSync) }
func TestSyncProgress63Fast(t *testing.T) { testSyncProgress(t, 63, FastSync) }
@@ -1524,7 +1424,6 @@ func testSyncProgress(t *testing.T, protocol int, mode SyncMode) {
// Tests that synchronisation progress (origin block number and highest block
// number) is tracked and updated correctly in case of a fork (or manual head
// revertal).
func TestForkedSyncProgress61(t *testing.T) { testForkedSyncProgress(t, 61, FullSync) }
func TestForkedSyncProgress62(t *testing.T) { testForkedSyncProgress(t, 62, FullSync) }
func TestForkedSyncProgress63Full(t *testing.T) { testForkedSyncProgress(t, 63, FullSync) }
func TestForkedSyncProgress63Fast(t *testing.T) { testForkedSyncProgress(t, 63, FastSync) }
@@ -1601,7 +1500,6 @@ func testForkedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
// Tests that if synchronisation is aborted due to some failure, then the progress
// origin is not updated in the next sync cycle, as it should be considered the
// continuation of the previous sync and not a new instance.
func TestFailedSyncProgress61(t *testing.T) { testFailedSyncProgress(t, 61, FullSync) }
func TestFailedSyncProgress62(t *testing.T) { testFailedSyncProgress(t, 62, FullSync) }
func TestFailedSyncProgress63Full(t *testing.T) { testFailedSyncProgress(t, 63, FullSync) }
func TestFailedSyncProgress63Fast(t *testing.T) { testFailedSyncProgress(t, 63, FastSync) }
@@ -1679,7 +1577,6 @@ func testFailedSyncProgress(t *testing.T, protocol int, mode SyncMode) {
// Tests that if an attacker fakes a chain height, after the attack is detected,
// the progress height is successfully reduced at the next sync invocation.
func TestFakedSyncProgress61(t *testing.T) { testFakedSyncProgress(t, 61, FullSync) }
func TestFakedSyncProgress62(t *testing.T) { testFakedSyncProgress(t, 62, FullSync) }
func TestFakedSyncProgress63Full(t *testing.T) { testFakedSyncProgress(t, 63, FullSync) }
func TestFakedSyncProgress63Fast(t *testing.T) { testFakedSyncProgress(t, 63, FastSync) }

View File

@@ -23,16 +23,6 @@ import (
)
var (
hashInMeter = metrics.NewMeter("eth/downloader/hashes/in")
hashReqTimer = metrics.NewTimer("eth/downloader/hashes/req")
hashDropMeter = metrics.NewMeter("eth/downloader/hashes/drop")
hashTimeoutMeter = metrics.NewMeter("eth/downloader/hashes/timeout")
blockInMeter = metrics.NewMeter("eth/downloader/blocks/in")
blockReqTimer = metrics.NewTimer("eth/downloader/blocks/req")
blockDropMeter = metrics.NewMeter("eth/downloader/blocks/drop")
blockTimeoutMeter = metrics.NewMeter("eth/downloader/blocks/timeout")
headerInMeter = metrics.NewMeter("eth/downloader/headers/in")
headerReqTimer = metrics.NewTimer("eth/downloader/headers/req")
headerDropMeter = metrics.NewMeter("eth/downloader/headers/drop")

View File

@@ -23,6 +23,7 @@ import (
"errors"
"fmt"
"math"
"math/big"
"sort"
"strings"
"sync"
@@ -37,10 +38,8 @@ const (
measurementImpact = 0.1 // The impact a single measurement has on a peer's final throughput value.
)
// Hash and block fetchers belonging to eth/61 and below
type relativeHashFetcherFn func(common.Hash) error
type absoluteHashFetcherFn func(uint64, int) error
type blockFetcherFn func([]common.Hash) error
// Head hash and total difficulty retriever for
type currentHeadRetrievalFn func() (common.Hash, *big.Int)
// Block header and body fetchers belonging to eth/62 and above
type relativeHeaderFetcherFn func(common.Hash, int, int, bool) error
@@ -57,8 +56,7 @@ var (
// peer represents an active peer from which hashes and blocks are retrieved.
type peer struct {
id string // Unique identifier of the peer
head common.Hash // Hash of the peers latest known block
id string // Unique identifier of the peer
headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1)
blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1)
@@ -79,9 +77,7 @@ type peer struct {
lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously)
getRelHashes relativeHashFetcherFn // [eth/61] Method to retrieve a batch of hashes from an origin hash
getAbsHashes absoluteHashFetcherFn // [eth/61] Method to retrieve a batch of hashes from an absolute position
getBlocks blockFetcherFn // [eth/61] Method to retrieve a batch of blocks
currentHead currentHeadRetrievalFn // Method to fetch the currently known head of the peer
getRelHeaders relativeHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an origin hash
getAbsHeaders absoluteHeaderFetcherFn // [eth/62] Method to retrieve a batch of headers from an absolute position
@@ -96,19 +92,14 @@ type peer struct {
// newPeer create a new downloader peer, with specific hash and block retrieval
// mechanisms.
func newPeer(id string, version int, head common.Hash,
getRelHashes relativeHashFetcherFn, getAbsHashes absoluteHashFetcherFn, getBlocks blockFetcherFn, // eth/61 callbacks, remove when upgrading
func newPeer(id string, version int, currentHead currentHeadRetrievalFn,
getRelHeaders relativeHeaderFetcherFn, getAbsHeaders absoluteHeaderFetcherFn, getBlockBodies blockBodyFetcherFn,
getReceipts receiptFetcherFn, getNodeData stateFetcherFn) *peer {
return &peer{
id: id,
head: head,
lacking: make(map[common.Hash]struct{}),
getRelHashes: getRelHashes,
getAbsHashes: getAbsHashes,
getBlocks: getBlocks,
currentHead: currentHead,
getRelHeaders: getRelHeaders,
getAbsHeaders: getAbsHeaders,
getBlockBodies: getBlockBodies,
@@ -138,28 +129,6 @@ func (p *peer) Reset() {
p.lacking = make(map[common.Hash]struct{})
}
// Fetch61 sends a block retrieval request to the remote peer.
func (p *peer) Fetch61(request *fetchRequest) error {
// Sanity check the protocol version
if p.version != 61 {
panic(fmt.Sprintf("block fetch [eth/61] requested on eth/%d", p.version))
}
// Short circuit if the peer is already fetching
if !atomic.CompareAndSwapInt32(&p.blockIdle, 0, 1) {
return errAlreadyFetching
}
p.blockStarted = time.Now()
// Convert the hash set to a retrievable slice
hashes := make([]common.Hash, 0, len(request.Hashes))
for hash, _ := range request.Hashes {
hashes = append(hashes, hash)
}
go p.getBlocks(hashes)
return nil
}
// FetchHeaders sends a header retrieval request to the remote peer.
func (p *peer) FetchHeaders(from uint64, count int) error {
// Sanity check the protocol version
@@ -481,20 +450,6 @@ func (ps *peerSet) AllPeers() []*peer {
return list
}
// BlockIdlePeers retrieves a flat list of all the currently idle peers within the
// active peer set, ordered by their reputation.
func (ps *peerSet) BlockIdlePeers() ([]*peer, int) {
idle := func(p *peer) bool {
return atomic.LoadInt32(&p.blockIdle) == 0
}
throughput := func(p *peer) float64 {
p.lock.RLock()
defer p.lock.RUnlock()
return p.blockThroughput
}
return ps.idlePeers(61, 61, idle, throughput)
}
// HeaderIdlePeers retrieves a flat list of all the currently header-idle peers
// within the active peer set, ordered by their reputation.
func (ps *peerSet) HeaderIdlePeers() ([]*peer, int) {

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