Compare commits
291 Commits
v1.4.0-alp
...
faucet-rat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a40ee2828a | ||
|
|
f219c080c7 | ||
|
|
75c18c9817 | ||
|
|
45c683fc1d | ||
|
|
c5f157cfad | ||
|
|
e18194720b | ||
|
|
a85215cd70 | ||
|
|
cd3539ab18 | ||
|
|
a9893492ba | ||
|
|
8fe7ca0b3b | ||
|
|
d98b22ba75 | ||
|
|
b844958a96 | ||
|
|
3cade73e40 | ||
|
|
4f38c78c6e | ||
|
|
7b8d28b425 | ||
|
|
74078e1dc4 | ||
|
|
21e6dcfc79 | ||
|
|
a262acfb00 | ||
|
|
87e622e51f | ||
|
|
13d454796f | ||
|
|
c6af48100d | ||
|
|
6d5b4ad64d | ||
|
|
d35b57ae36 | ||
|
|
21fc2d3ac4 | ||
|
|
c96fab04a3 | ||
|
|
27d86948fa | ||
|
|
a04e287cb6 | ||
|
|
c3d6155fff | ||
|
|
a00ffa762c | ||
|
|
863fdea026 | ||
|
|
90e67970ae | ||
|
|
e8456c2d08 | ||
|
|
bc970e5893 | ||
|
|
a5810fefc9 | ||
|
|
971c0fa380 | ||
|
|
88225c1a4b | ||
|
|
51e27f9e3b | ||
|
|
f1a85ec306 | ||
|
|
727c07116d | ||
|
|
c2226a0c9f | ||
|
|
d52628aa82 | ||
|
|
f7de51f74e | ||
|
|
55cbf31f18 | ||
|
|
1548452def | ||
|
|
f2e7f1dd24 | ||
|
|
27a3ec5d72 | ||
|
|
6094d7157e | ||
|
|
be0fbfb79e | ||
|
|
00f094c37e | ||
|
|
7b08a70a23 | ||
|
|
99d31aeb28 | ||
|
|
f467c6018b | ||
|
|
aab4b8812a | ||
|
|
af7e9b95bd | ||
|
|
1047f0e59a | ||
|
|
9bb4fed1bf | ||
|
|
35e71a769b | ||
|
|
6b02ac7ac5 | ||
|
|
8b9558bb4d | ||
|
|
63e7eac394 | ||
|
|
05543e558d | ||
|
|
b0146261c7 | ||
|
|
d7b9866d3b | ||
|
|
f5ba30ed47 | ||
|
|
f190c49252 | ||
|
|
08769ead2b | ||
|
|
4d0f1e7117 | ||
|
|
c77bb1110d | ||
|
|
c856d21719 | ||
|
|
f45305b1ad | ||
|
|
d16532d678 | ||
|
|
5edd032cdb | ||
|
|
6b8cbbe172 | ||
|
|
5ea2ada0ee | ||
|
|
b230a02006 | ||
|
|
86e3a02490 | ||
|
|
0c0958ff87 | ||
|
|
c577ce3720 | ||
|
|
d436f9e2e8 | ||
|
|
97c3b9b267 | ||
|
|
0560685460 | ||
|
|
bf16a39876 | ||
|
|
2c8720016d | ||
|
|
f2ec3cc6a5 | ||
|
|
0a2e1282d2 | ||
|
|
adb5e8fe86 | ||
|
|
23f6194fad | ||
|
|
691d195526 | ||
|
|
b57c779759 | ||
|
|
4ab1c865b2 | ||
|
|
a7d5b02919 | ||
|
|
1ce9bb044d | ||
|
|
7948950f7a | ||
|
|
0c101e618a | ||
|
|
571ea2c4b9 | ||
|
|
7bc5a3353d | ||
|
|
901ea2e0d2 | ||
|
|
1d81f3316f | ||
|
|
43b2ffa63b | ||
|
|
0567715760 | ||
|
|
e32fcf5b93 | ||
|
|
e55028d788 | ||
|
|
9d8df917b8 | ||
|
|
9e170972f4 | ||
|
|
ba6726325a | ||
|
|
6573254a62 | ||
|
|
31d92c50ad | ||
|
|
7cab9c622c | ||
|
|
2a0e399c38 | ||
|
|
182c841374 | ||
|
|
14023fae6d | ||
|
|
d653cda82e | ||
|
|
4b54601d5c | ||
|
|
3b7f0e4279 | ||
|
|
fe1fff8c77 | ||
|
|
c0afdc9a98 | ||
|
|
fb435eb5f1 | ||
|
|
5cc253a2cd | ||
|
|
cbcd26c9a9 | ||
|
|
90eb5b33e8 | ||
|
|
837de88057 | ||
|
|
b4fb2f6ffc | ||
|
|
11503edeb2 | ||
|
|
3a6e3c67f2 | ||
|
|
335be39905 | ||
|
|
b7972bcd77 | ||
|
|
4bb1bd1a77 | ||
|
|
a05724588f | ||
|
|
009df5a121 | ||
|
|
d7836bfe98 | ||
|
|
f4bad20447 | ||
|
|
a75e82367d | ||
|
|
26f50099f4 | ||
|
|
060e5c6b34 | ||
|
|
4e9f699068 | ||
|
|
42a0236587 | ||
|
|
48f58a50bb | ||
|
|
e4688e4e7a | ||
|
|
75a03f420f | ||
|
|
46df9b4dcb | ||
|
|
7dbafe7453 | ||
|
|
f2c9141e4f | ||
|
|
c3623e9af7 | ||
|
|
32ac07f257 | ||
|
|
3b7ee60e14 | ||
|
|
438cdf0861 | ||
|
|
212b7a6972 | ||
|
|
7c7a9bc53b | ||
|
|
04a3b1f94f | ||
|
|
5d51873890 | ||
|
|
3e97f827b4 | ||
|
|
9ab8565128 | ||
|
|
7c89c65a97 | ||
|
|
e7c5ce2e94 | ||
|
|
eda56e22a9 | ||
|
|
dddf20e6e0 | ||
|
|
79cd5222e7 | ||
|
|
38db9bf4e2 | ||
|
|
e0b98ef9cb | ||
|
|
7f3f72ed41 | ||
|
|
76fb29504c | ||
|
|
84c36588cd | ||
|
|
22888c8725 | ||
|
|
e313d5b319 | ||
|
|
1208d07e94 | ||
|
|
fd284c74dd | ||
|
|
fdbe2e3cb0 | ||
|
|
9684ba3a83 | ||
|
|
1324884db7 | ||
|
|
ebe88c09a9 | ||
|
|
66ed85ef82 | ||
|
|
2893079aa4 | ||
|
|
3c81d559e7 | ||
|
|
c1496e7ced | ||
|
|
291ab99d4a | ||
|
|
e6e1d06687 | ||
|
|
c170814596 | ||
|
|
09b469f0bf | ||
|
|
6978f009ab | ||
|
|
74d20546c3 | ||
|
|
ccd7a44be0 | ||
|
|
10ae179a73 | ||
|
|
16b040e8c4 | ||
|
|
880f7ab865 | ||
|
|
58bc494fa7 | ||
|
|
d9b9b7f66b | ||
|
|
e4ddf5881b | ||
|
|
a4b436806e | ||
|
|
cbff31944b | ||
|
|
7169e2b864 | ||
|
|
d620407ff4 | ||
|
|
9e85b0f29f | ||
|
|
eb4465567e | ||
|
|
fd914c2330 | ||
|
|
4387eec4dc | ||
|
|
8ef781a41e | ||
|
|
1487e46f30 | ||
|
|
270793f13a | ||
|
|
31ace32e9e | ||
|
|
b7d3be0398 | ||
|
|
6386172b95 | ||
|
|
4051c34e18 | ||
|
|
16c6e462e0 | ||
|
|
def3512fd8 | ||
|
|
411d5c5001 | ||
|
|
89c4ab2a05 | ||
|
|
0dc4b1f119 | ||
|
|
b75720d270 | ||
|
|
a18ed24b9d | ||
|
|
38d592dfdd | ||
|
|
2bd6bd01d2 | ||
|
|
9038ba6942 | ||
|
|
51b479e564 | ||
|
|
5a0f468f8c | ||
|
|
45a272c7b9 | ||
|
|
63aaac8100 | ||
|
|
c1f59b98f6 | ||
|
|
821d70240d | ||
|
|
8bca93e82c | ||
|
|
edffacca8f | ||
|
|
26724fc2aa | ||
|
|
32d4d6e616 | ||
|
|
93c541ad56 | ||
|
|
b87b9b4533 | ||
|
|
e47a7c22c4 | ||
|
|
b590cae892 | ||
|
|
7f131dcbc9 | ||
|
|
3b4ede7444 | ||
|
|
b47cf8fe1d | ||
|
|
b9ca38b735 | ||
|
|
79e340fb12 | ||
|
|
bba3fa9af9 | ||
|
|
7f5e96dc6c | ||
|
|
f4852b8ddc | ||
|
|
ac0ff04460 | ||
|
|
6fb0d0992b | ||
|
|
5d984796af | ||
|
|
034bc4669f | ||
|
|
593e303485 | ||
|
|
95741b1844 | ||
|
|
3c30de219f | ||
|
|
a193bb0c73 | ||
|
|
1bdf8b9b2d | ||
|
|
0c412dcd1f | ||
|
|
286090689a | ||
|
|
886f0e72e5 | ||
|
|
9e3e46671e | ||
|
|
2a1d94bd1d | ||
|
|
efddedc16c | ||
|
|
9d537f5439 | ||
|
|
8321fe2fda | ||
|
|
55a46c3b10 | ||
|
|
fe91d476ba | ||
|
|
4c15d58007 | ||
|
|
beb2954fa4 | ||
|
|
f1c27c286e | ||
|
|
1a79089193 | ||
|
|
f0c5b6765d | ||
|
|
89575aeb4b | ||
|
|
02eb36afc2 | ||
|
|
8facf44109 | ||
|
|
85938dda09 | ||
|
|
ac5aa672d3 | ||
|
|
2732fb10d2 | ||
|
|
8a76a814a2 | ||
|
|
ae3b7a0b65 | ||
|
|
2dc33d46b8 | ||
|
|
2ab365f6d8 | ||
|
|
69f5d5ba1f | ||
|
|
449d3f0d87 | ||
|
|
1f50aa7631 | ||
|
|
199e0c9ff5 | ||
|
|
16ce7bf50f | ||
|
|
0b5d8d2b58 | ||
|
|
99e9c0702b | ||
|
|
8fd43c8013 | ||
|
|
8ec638dc5e | ||
|
|
19af9008f1 | ||
|
|
253447a4f5 | ||
|
|
47d76c5f95 | ||
|
|
62affdc9c5 | ||
|
|
06a871136e | ||
|
|
5c67066a05 | ||
|
|
3adf1cecf2 | ||
|
|
eaac53ec38 | ||
|
|
fc380f52ef | ||
|
|
e2778cd59f | ||
|
|
db98cc485e | ||
|
|
2e947b7a00 | ||
|
|
bc0b87ca19 | ||
|
|
cd0770ea68 |
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -1,6 +1,5 @@
|
||||
# Lines starting with '#' are comments.
|
||||
# Each line is a file pattern followed by one or more owners.
|
||||
|
||||
accounts/usbwallet @karalabe
|
||||
accounts/scwallet @gballet
|
||||
accounts/abi @gballet @MariusVanDerWijden
|
||||
|
||||
2
.nancy-ignore
Normal file
2
.nancy-ignore
Normal file
@@ -0,0 +1,2 @@
|
||||
CVE-2024-34478 # "CWE-754: Improper Check for Unusual or Exceptional Conditions." This vulnerability is BTC only, BSC does not have the issue.
|
||||
CVE-2024-6104 # "CWE-532: Information Exposure Through Log Files" This is caused by the vulnerabilities go-retryablehttp@v0.7.4, it is only used in cmd devp2p, impact is limited. will upgrade to v0.7.7 later
|
||||
234
CHANGELOG.md
234
CHANGELOG.md
@@ -1,4 +1,234 @@
|
||||
# Changelog
|
||||
|
||||
## v1.4.12
|
||||
|
||||
### BUGFIX
|
||||
* [\#2557](https://github.com/bnb-chain/bsc/pull/2557) fix: fix state inspect error after pruned state
|
||||
* [\#2562](https://github.com/bnb-chain/bsc/pull/2562) fix: delete unexpected block
|
||||
* [\#2566](https://github.com/bnb-chain/bsc/pull/2566) core: avoid to cache block before wroten into db
|
||||
* [\#2567](https://github.com/bnb-chain/bsc/pull/2567) fix: fix statedb copy
|
||||
* [\#2574](https://github.com/bnb-chain/bsc/pull/2574) core: adapt highestVerifiedHeader to FastFinality
|
||||
* [\#2542](https://github.com/bnb-chain/bsc/pull/2542) fix: pruneancient freeze from the previous position when the first time
|
||||
* [\#2564](https://github.com/bnb-chain/bsc/pull/2564) fix: the bug of blobsidecars and downloader with multi-database
|
||||
* [\#2582](https://github.com/bnb-chain/bsc/pull/2582) fix: remove delete and dangling side chains in prunefreezer
|
||||
|
||||
### FEATURE
|
||||
* [\#2513](https://github.com/bnb-chain/bsc/pull/2513) cmd/jsutils: add a tool to get performance between a range of blocks
|
||||
* [\#2569](https://github.com/bnb-chain/bsc/pull/2569) cmd/jsutils: add a tool to get slash count
|
||||
* [\#2583](https://github.com/bnb-chain/bsc/pull/2583) cmd/jsutill: add log about validator name
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2546](https://github.com/bnb-chain/bsc/pull/2546) go.mod: update missing dependency
|
||||
* [\#2559](https://github.com/bnb-chain/bsc/pull/2559) nancy: ignore go-retryablehttp@v0.7.4 in .nancy-ignore
|
||||
* [\#2556](https://github.com/bnb-chain/bsc/pull/2556) chore: update greenfield cometbft version
|
||||
* [\#2561](https://github.com/bnb-chain/bsc/pull/2561) tests: fix unstable test
|
||||
* [\#2572](https://github.com/bnb-chain/bsc/pull/2572) core: clearup testflag for Cancun and Haber
|
||||
* [\#2573](https://github.com/bnb-chain/bsc/pull/2573) cmd/utils: support use NetworkId to distinguish chapel when do syncing
|
||||
* [\#2538](https://github.com/bnb-chain/bsc/pull/2538) feat: enhance bid comparison and reply bidding results && detail logs
|
||||
* [\#2568](https://github.com/bnb-chain/bsc/pull/2568) core/vote: not vote if too late for next in turn validator
|
||||
* [\#2576](https://github.com/bnb-chain/bsc/pull/2576) miner/worker: broadcast block immediately once sealed
|
||||
* [\#2580](https://github.com/bnb-chain/bsc/pull/2580) freezer: Opt freezer env checking
|
||||
|
||||
## v1.4.11
|
||||
|
||||
### BUGFIX
|
||||
* [\#2534](https://github.com/bnb-chain/bsc/pull/2534) fix: nil pointer when clear simulating bid
|
||||
* [\#2535](https://github.com/bnb-chain/bsc/pull/2535) upgrade: add HaberFix hardfork
|
||||
|
||||
|
||||
## v1.4.10
|
||||
### FEATURE
|
||||
NA
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2512](https://github.com/bnb-chain/bsc/pull/2512) feat: add mev helper params and func
|
||||
* [\#2508](https://github.com/bnb-chain/bsc/pull/2508) perf: speedup pbss trienode read
|
||||
* [\#2509](https://github.com/bnb-chain/bsc/pull/2509) perf: optimize chain commit performance for multi-database
|
||||
* [\#2451](https://github.com/bnb-chain/bsc/pull/2451) core/forkchoice: improve stability when inturn block not generate
|
||||
|
||||
### BUGFIX
|
||||
* [\#2518](https://github.com/bnb-chain/bsc/pull/2518) fix: remove zero gasprice check for BSC
|
||||
* [\#2519](https://github.com/bnb-chain/bsc/pull/2519) UT: random failure of TestSnapSyncWithBlobs
|
||||
* [\#2515](https://github.com/bnb-chain/bsc/pull/2515) fix getBlobSidecars by ethclient
|
||||
* [\#2525](https://github.com/bnb-chain/bsc/pull/2525) fix: ensure empty withdrawals after cancun before broadcast
|
||||
|
||||
## v1.4.9
|
||||
### FEATURE
|
||||
* [\#2463](https://github.com/bnb-chain/bsc/pull/2463) utils: add check_blobtx.js
|
||||
* [\#2470](https://github.com/bnb-chain/bsc/pull/2470) jsutils: faucet successful requests within blocks
|
||||
* [\#2467](https://github.com/bnb-chain/bsc/pull/2467) internal/ethapi: add optional parameter for blobSidecars
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2462](https://github.com/bnb-chain/bsc/pull/2462) cmd/utils: add a flag to change breathe block interval for testing
|
||||
* [\#2497](https://github.com/bnb-chain/bsc/pull/2497) params/config: add Bohr hardfork
|
||||
* [\#2479](https://github.com/bnb-chain/bsc/pull/2479) dev: ensure consistency in BPS bundle result
|
||||
|
||||
### BUGFIX
|
||||
* [\#2461](https://github.com/bnb-chain/bsc/pull/2461) eth/handler: check lists in body before broadcast blocks
|
||||
* [\#2455](https://github.com/bnb-chain/bsc/pull/2455) cmd: fix memory leak when big dataset
|
||||
* [\#2466](https://github.com/bnb-chain/bsc/pull/2466) sync: fix some sync issues caused by prune-block.
|
||||
* [\#2475](https://github.com/bnb-chain/bsc/pull/2475) fix: move mev op to MinerAPI & add command to console
|
||||
* [\#2473](https://github.com/bnb-chain/bsc/pull/2473) fix: limit the gas price of the mev bid
|
||||
* [\#2484](https://github.com/bnb-chain/bsc/pull/2484) fix: fix inspect database error
|
||||
* [\#2481](https://github.com/bnb-chain/bsc/pull/2481) fix: keep 9W blocks in ancient db when prune block
|
||||
* [\#2495](https://github.com/bnb-chain/bsc/pull/2495) fix: add an empty freeze db
|
||||
* [\#2507](https://github.com/bnb-chain/bsc/pull/2507) fix: waiting for the last simulation before pick best bid
|
||||
|
||||
## v1.4.8
|
||||
### FEATURE
|
||||
* [\#2483](https://github.com/bnb-chain/bsc/pull/2483) core/vm: add secp256r1 into PrecompiledContractsHaber
|
||||
* [\#2400](https://github.com/bnb-chain/bsc/pull/2400) RIP-7212: Precompile for secp256r1 Curve Support
|
||||
|
||||
### IMPROVEMENT
|
||||
NA
|
||||
|
||||
### BUGFIX
|
||||
NA
|
||||
|
||||
## v1.4.7
|
||||
### FEATURE
|
||||
* [\#2439](https://github.com/bnb-chain/bsc/pull/2439) config: setup Mainnet Tycho(Cancun) hardfork date
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2396](https://github.com/bnb-chain/bsc/pull/2396) metrics: add blockInsertMgaspsGauge to trace mgasps
|
||||
* [\#2411](https://github.com/bnb-chain/bsc/pull/2411) build(deps): bump golang.org/x/net from 0.19.0 to 0.23.0
|
||||
* [\#2435](https://github.com/bnb-chain/bsc/pull/2435) txpool: limit max gas when mining is enabled
|
||||
* [\#2438](https://github.com/bnb-chain/bsc/pull/2438) fix: performance issue when load journal
|
||||
* [\#2440](https://github.com/bnb-chain/bsc/pull/2440) nancy: add files .nancy-ignore
|
||||
|
||||
### BUGFIX
|
||||
NA
|
||||
|
||||
## v1.4.6
|
||||
### FEATURE
|
||||
* [\#2227](https://github.com/bnb-chain/bsc/pull/2227) core: separated databases for block data
|
||||
* [\#2404](https://github.com/bnb-chain/bsc/pull/2404) cmd, p2p: filter peers by regex on name
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2201](https://github.com/bnb-chain/bsc/pull/2201) chore: render system bytecode by go:embed
|
||||
* [\#2363](https://github.com/bnb-chain/bsc/pull/2363) feat: greedy merge tx in bid
|
||||
* [\#2389](https://github.com/bnb-chain/bsc/pull/2389) deps: update prsym to solve warning about quic-go version
|
||||
* [\#2341](https://github.com/bnb-chain/bsc/pull/2341) core/trie: persist TrieJournal to journal file instead of kv database
|
||||
* [\#2395](https://github.com/bnb-chain/bsc/pull/2395) fix: trieJournal format compatible old db format
|
||||
* [\#2406](https://github.com/bnb-chain/bsc/pull/2406) feat: adaptive for loading journal file or journal kv during loadJournal
|
||||
* [\#2390](https://github.com/bnb-chain/bsc/pull/2390) chore: fix function names in comment
|
||||
* [\#2399](https://github.com/bnb-chain/bsc/pull/2399) chore: fix some typos in comments
|
||||
* [\#2408](https://github.com/bnb-chain/bsc/pull/2408) chore: fix some typos in comments
|
||||
* [\#2416](https://github.com/bnb-chain/bsc/pull/2416) fix: fix function names
|
||||
* [\#2424](https://github.com/bnb-chain/bsc/pull/2424) feat: recommit bid when newBidCh is empty to maximize mev reward
|
||||
* [\#2430](https://github.com/bnb-chain/bsc/pull/2430) fix: oom caused by non-discarded mev simulation env
|
||||
* [\#2428](https://github.com/bnb-chain/bsc/pull/2428) chore: add metric & log for blobTx
|
||||
* [\#2419](https://github.com/bnb-chain/bsc/pull/2419) metrics: add doublesign counter
|
||||
|
||||
### BUGFIX
|
||||
* [\#2244](https://github.com/bnb-chain/bsc/pull/2244) cmd/geth: fix importBlock
|
||||
* [\#2391](https://github.com/bnb-chain/bsc/pull/2391) fix: print value instead of pointer in ConfigCompatError
|
||||
* [\#2398](https://github.com/bnb-chain/bsc/pull/2398) fix: no import blocks before or equal to the finalized height
|
||||
* [\#2401](https://github.com/bnb-chain/bsc/pull/2401) fix: allow fast node to rewind after abnormal shutdown
|
||||
* [\#2403](https://github.com/bnb-chain/bsc/pull/2403) fix: NPE
|
||||
* [\#2423](https://github.com/bnb-chain/bsc/pull/2423) eth/gasprice: add query limit to defend DDOS attack
|
||||
* [\#2425](https://github.com/bnb-chain/bsc/pull/2425) fix: adapt journal for cmd
|
||||
|
||||
## v1.4.5
|
||||
### FEATURE
|
||||
* [\#2378](https://github.com/bnb-chain/bsc/pull/2378) config: setup Testnet Tycho(Cancun) hardfork date
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2333](https://github.com/bnb-chain/bsc/pull/2333) remove code that will not be executed
|
||||
* [\#2369](https://github.com/bnb-chain/bsc/pull/2369) core: stateDb has no trie and no snap return err
|
||||
|
||||
### BUGFIX
|
||||
* [\#2359](https://github.com/bnb-chain/bsc/pull/2359) triedb: do not open state freezer under notries
|
||||
|
||||
## v1.4.4
|
||||
### FEATURE
|
||||
* [\#2279](https://github.com/bnb-chain/bsc/pull/2279) BlobTx: implement EIP-4844 on BSC
|
||||
* [\#2337](https://github.com/bnb-chain/bsc/pull/2337) 4844: bugfix and improve
|
||||
* [\#2339](https://github.com/bnb-chain/bsc/pull/2339) fix: missing block asigment WithSidecars
|
||||
* [\#2350](https://github.com/bnb-chain/bsc/pull/2350) cancun: change empty withdrawHash value of header
|
||||
* [\#2335](https://github.com/bnb-chain/bsc/pull/2335) upgrade: update system contracts bytes code and hardfork time of Feynman upgrade
|
||||
* [\#2323](https://github.com/bnb-chain/bsc/pull/2323) feat: export GasCeil in mev_params
|
||||
* [\#2357](https://github.com/bnb-chain/bsc/pull/2357) feat: add bid fee ceil in mev_params
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2321](https://github.com/bnb-chain/bsc/pull/2321) test: use full syncmode to run rpc node
|
||||
* [\#2338](https://github.com/bnb-chain/bsc/pull/2338) cmd: include more node info in metrics
|
||||
* [\#2342](https://github.com/bnb-chain/bsc/pull/2342) p2p: add metrics for inbound/outbound peers
|
||||
* [\#2334](https://github.com/bnb-chain/bsc/pull/2334) core: improve chain rewinding mechanism
|
||||
* [\#2352](https://github.com/bnb-chain/bsc/pull/2352) core: fix block report when chain is not setHead
|
||||
|
||||
### BUGFIX
|
||||
NA
|
||||
|
||||
## v1.4.3
|
||||
### FEATURE
|
||||
* [\#2241](https://github.com/bnb-chain/bsc/pull/2241) cmd/utils, core/rawdb, triedb/pathdb: flip hash to path scheme
|
||||
* [\#2312](https://github.com/bnb-chain/bsc/pull/2312) cmd/utils, node: switch to Pebble as the default db if none exists
|
||||
|
||||
### IMPROVEMENT
|
||||
* [\#2228](https://github.com/bnb-chain/bsc/pull/2228) core: rephrase TriesInMemory log
|
||||
* [\#2234](https://github.com/bnb-chain/bsc/pull/2234) cmd/utils: disable snap protocol for fast node
|
||||
* [\#2236](https://github.com/bnb-chain/bsc/pull/2236) build(deps): bump github.com/quic-go/quic-go from 0.39.3 to 0.39.4
|
||||
* [\#2240](https://github.com/bnb-chain/bsc/pull/2240) core/state: fix taskResult typo
|
||||
|
||||
* [\#2280](https://github.com/bnb-chain/bsc/pull/2280) cmd/utils, core: only full sync for fast nodes
|
||||
* [\#2298](https://github.com/bnb-chain/bsc/pull/2298) cmd, node: initialize ports with --instance
|
||||
* [\#2302](https://github.com/bnb-chain/bsc/pull/2302) cmd/geth, core/rawdb: add dbDeleteTrieState
|
||||
* [\#2304](https://github.com/bnb-chain/bsc/pull/2304) eth/ethconfig: remove overridekepler and overrideshanghai
|
||||
* [\#2307](https://github.com/bnb-chain/bsc/pull/2307) internal/ethapi: add net_nodeInfo
|
||||
* [\#2311](https://github.com/bnb-chain/bsc/pull/2311) Port cancun related changes from unreleased v1.14.0
|
||||
* [\#2313](https://github.com/bnb-chain/bsc/pull/2313) tests/truffle: use hbss to run test
|
||||
* [\#2314](https://github.com/bnb-chain/bsc/pull/2314) cmd/jsutil: dump MinGasPrice for validator
|
||||
* [\#2317](https://github.com/bnb-chain/bsc/pull/2317) feat: add mev metrics
|
||||
|
||||
### BUGFIX
|
||||
* [\#2272](https://github.com/bnb-chain/bsc/pull/2272) parlia: add state prepare for internal SC transaction
|
||||
* [\#2277](https://github.com/bnb-chain/bsc/pull/2277) fix: systemTx should be always at the end of block
|
||||
* [\#2299](https://github.com/bnb-chain/bsc/pull/2299) fix: add FeynmanFix upgrade for a testnet issue
|
||||
* [\#2310](https://github.com/bnb-chain/bsc/pull/2310) core/vm: fix PrecompiledContractsCancun
|
||||
|
||||
## v1.4.2
|
||||
### FEATURE
|
||||
* [\#2021](https://github.com/bnb-chain/bsc/pull/2021) feat: support separate trie database
|
||||
* [\#2224](https://github.com/bnb-chain/bsc/pull/2224) feat: support MEV
|
||||
|
||||
### BUGFIX
|
||||
* [\#2268](https://github.com/bnb-chain/bsc/pull/2268) fix: ensure EIP-4788 not supported with Parlia Engine
|
||||
|
||||
### Cancun Code Merge
|
||||
#### 4844 related
|
||||
[internal/ethapi: add support for blobs in eth_fillTransaction (#28839)](https://github.com/bnb-chain/bsc/commit/ac5aa672d3b85a1f74667a65a15398f072aa0b2a)
|
||||
[internal/ethapi: fix defaults for blob fields (#29037)](https://github.com/bnb-chain/bsc/commit/b47cf8fe1de4f97ce38417d8136a58812734a7a9)
|
||||
[ethereum, ethclient: add blob transaction fields in CallMsg (#28989)](https://github.com/bnb-chain/bsc/commit/9d537f543990d9013d73433dc58fd0e985d9b2b6)
|
||||
[core/txpool/blobpool: post-crash cleanup and addition/removal metrics(#28914)](https://github.com/bnb-chain/bsc/commit/62affdc9c5ea6f1a73fde42ac5ee5c9795877f88)
|
||||
[core/txpool/blobpool: update the blob db with corruption handling (#29001)](https://github.com/bnb-chain/bsc/commit/3c30de219f92120248b7b7aeeb2bef82305e9627)
|
||||
[core/txpool, eth, miner: pre-filter dynamic fees during pending tx retrieval (#29005)](https://github.com/bnb-chain/bsc/commit/593e303485473d9b9194792e4556a451c44dcc6c)
|
||||
[core/txpool, miner: speed up blob pool pending retrievals (#29008)](https://github.com/bnb-chain/bsc/commit/6fb0d0992bd4eb91faf1e081b3c4aa46adb0ef7d)
|
||||
[core/txpool, eth, miner: retrieve plain and blob txs separately (#29026)](https://github.com/bnb-chain/bsc/commit/f4852b8ddc8bef962d34210a4f7774b95767e421)
|
||||
[core/txpool: reject blob txs with blob fee cap below the minimum (#29081)](https://github.com/bnb-chain/bsc/commit/32d4d6e6160432be1cb9780a43253deda7708ced)
|
||||
[core/txpool/blobpool: reduce default database cap for rollout (#29090)](https://github.com/bnb-chain/bsc/commit/63aaac81007ad46b208570c17cae78b7f60931d4)
|
||||
#### Clean Ups
|
||||
[cmd/devp2p, eth: drop support for eth/67 (#28956)](https://github.com/bnb-chain/bsc/commit/8a76a814a2b9e5b4c1a4c6de44cd702536104507)
|
||||
[all: remove the dependency from trie to triedb (#28824)](https://github.com/bnb-chain/bsc/commit/fe91d476ba3e29316b6dc99b6efd4a571481d888)
|
||||
#### Others
|
||||
[eth, miner: fix enforcing the minimum miner tip (#28933)](https://github.com/bnb-chain/bsc/commit/16ce7bf50fa71c907d1dc6504ed32a9161e71351)
|
||||
[cmd,internal/era: implement export-history subcommand(#26621)](https://github.com/bnb-chain/bsc/commit/1f50aa76318689c6e74d0c3b4f31421bf7382fc7)
|
||||
[node, rpc: add configurable HTTP request limit (#28948)](https://github.com/bnb-chain/bsc/commit/69f5d5ba1fe355ff7e3dee5a0c7e662cd82f1071)
|
||||
[tests: fix goroutine leak related to state snapshot generation (#28974)](https://github.com/bnb-chain/bsc/commit/8321fe2fda0b44d6df3750bcee28b8627525173b)
|
||||
[internal/ethapi:fix zero rpc gas cap in eth_createAccessList (#28846)](https://github.com/bnb-chain/bsc/commit/b87b9b45331f87fb1da379c5f17a81ebc3738c6e)
|
||||
[eth/tracers: Fix callTracer logs on onlyTopCall == true (#29068)](https://github.com/bnb-chain/bsc/commit/5a0f468f8cb15b939bd85445d33c614a36942a8e)
|
||||
|
||||
## v1.4.1
|
||||
FEATURE
|
||||
NA
|
||||
|
||||
BUGFIX
|
||||
* [\#2258](https://github.com/bnb-chain/bsc/pull/2258) core: skip checking state root existence when do snapsync by fast node
|
||||
* [\#2252](https://github.com/bnb-chain/bsc/pull/2252) fix: add missing args of `bls account generate-proof` cmd (#2252)
|
||||
|
||||
IMPROVEMENT
|
||||
NA
|
||||
|
||||
## v1.4.0
|
||||
#### RPC
|
||||
[internal/ethapi: implement eth_getBlockReceipts (#27702)](https://github.com/bnb-chain/bsc/commit/f1801a9feda8f81532c92077d2c9a8b785fd699b)
|
||||
@@ -78,6 +308,10 @@
|
||||
[event: fix Resubscribe deadlock when unsubscribing after inner sub ends (#28359)](https://github.com/bnb-chain/bsc/commit/ffc6a0f36edda396a8421cf7a3c0feb88be20d0b)
|
||||
[all: replace log15 with slog (#28187)](https://github.com/bnb-chain/bsc/commit/28e73717016cdc9ebdb5fdb3474cfbd3bd2d2524)
|
||||
|
||||
## v1.3.11
|
||||
BUGFIX
|
||||
* [\#2288](https://github.com/bnb-chain/bsc/pull/2288) fix: add FeynmanFix upgrade for a testnet issue
|
||||
|
||||
## v1.3.10
|
||||
FEATURE
|
||||
* [\#2047](https://github.com/bnb-chain/bsc/pull/2047) feat: add new fork block and precompile contract for BEP294 and BEP299
|
||||
|
||||
@@ -26,7 +26,7 @@ COPY --from=builder /go-ethereum/build/bin/* /usr/local/bin/
|
||||
|
||||
EXPOSE 8545 8546 30303 30303/udp
|
||||
|
||||
# Add some metadata labels to help programatic image consumption
|
||||
# Add some metadata labels to help programmatic image consumption
|
||||
ARG COMMIT=""
|
||||
ARG VERSION=""
|
||||
ARG BUILDNUM=""
|
||||
|
||||
13
Makefile
13
Makefile
@@ -11,14 +11,17 @@ GORUN = go run
|
||||
GIT_COMMIT=$(shell git rev-parse HEAD)
|
||||
GIT_COMMIT_DATE=$(shell git log -n1 --pretty='format:%cd' --date=format:'%Y%m%d')
|
||||
|
||||
#? geth: Build geth
|
||||
geth:
|
||||
$(GORUN) build/ci.go install ./cmd/geth
|
||||
@echo "Done building."
|
||||
@echo "Run \"$(GOBIN)/geth\" to launch geth."
|
||||
|
||||
#? all: Build all packages and executables
|
||||
all:
|
||||
$(GORUN) build/ci.go install
|
||||
|
||||
#? test: Run the tests
|
||||
test: all
|
||||
$(GORUN) build/ci.go test -timeout 1h
|
||||
|
||||
@@ -32,9 +35,11 @@ truffle-test:
|
||||
docker-compose -f ./tests/truffle/docker-compose.yml up --exit-code-from truffle-test truffle-test
|
||||
docker-compose -f ./tests/truffle/docker-compose.yml down
|
||||
|
||||
#? lint: Run certain pre-selected linters
|
||||
lint: ## Run linters.
|
||||
$(GORUN) build/ci.go lint
|
||||
|
||||
#? clean: Clean go cache, built executables, and the auto generated folder
|
||||
clean:
|
||||
go clean -cache
|
||||
rm -fr build/_workspace/pkg/ $(GOBIN)/*
|
||||
@@ -42,6 +47,7 @@ clean:
|
||||
# The devtools target installs tools required for 'go generate'.
|
||||
# You need to put $GOBIN (or $GOPATH/bin) in your PATH to use 'go generate'.
|
||||
|
||||
#? devtools: Install recommended developer tools
|
||||
devtools:
|
||||
env GOBIN= go install golang.org/x/tools/cmd/stringer@latest
|
||||
env GOBIN= go install github.com/fjl/gencodec@latest
|
||||
@@ -50,5 +56,12 @@ devtools:
|
||||
@type "solc" 2> /dev/null || echo 'Please install solc'
|
||||
@type "protoc" 2> /dev/null || echo 'Please install protoc'
|
||||
|
||||
#? help: Build docker image
|
||||
docker:
|
||||
docker build --pull -t bnb-chain/bsc:latest -f Dockerfile .
|
||||
|
||||
#? help: Get more info on make commands.
|
||||
help: Makefile
|
||||
@echo " Choose a command run in go-ethereum:"
|
||||
@sed -n 's/^#?//p' $< | column -t -s ':' | sort | sed -e 's/^/ /'
|
||||
.PHONY: help
|
||||
|
||||
@@ -36,7 +36,7 @@ To combine DPoS and PoA for consensus, BNB Smart Chain implement a novel consens
|
||||
2. Validators take turns to produce blocks in a PoA manner, similar to Ethereum's Clique consensus engine.
|
||||
3. Validator set are elected in and out based on a staking based governance on BNB Beacon Chain.
|
||||
4. The validator set change is relayed via a cross-chain communication mechanism.
|
||||
5. Parlia consensus engine will interact with a set of [system contracts](https://docs.bnbchain.org/docs/learn/system-contract) to achieve liveness slash, revenue distributing and validator set renewing func.
|
||||
5. Parlia consensus engine will interact with a set of [system contracts](https://docs.bnbchain.org/bnb-smart-chain/staking/overview/#system-contracts) to achieve liveness slash, revenue distributing and validator set renewing func.
|
||||
|
||||
|
||||
### Light Client of BNB Beacon Chain
|
||||
@@ -149,13 +149,11 @@ unzip testnet.zip
|
||||
#### 3. Download snapshot
|
||||
Download latest chaindata snapshot from [here](https://github.com/bnb-chain/bsc-snapshots). Follow the guide to structure your files.
|
||||
|
||||
Note: If you encounter difficulties downloading the chaindata snapshot and prefer to synchronize from the genesis block on the Chapel testnet, remember to include the additional flag `--chapel` when initially launching Geth.
|
||||
|
||||
#### 4. Start a full node
|
||||
```shell
|
||||
./geth --config ./config.toml --datadir ./node --cache 8000 --rpc.allow-unprotected-txs --history.transactions 0
|
||||
|
||||
## It is recommand to run fullnode with `--tries-verify-mode none` if you want high performance and care little about state consistency
|
||||
## It is recommend to run fullnode with `--tries-verify-mode none` if you want high performance and care little about state consistency
|
||||
## It will run with Hash-Base Storage Scheme by default
|
||||
./geth --config ./config.toml --datadir ./node --cache 8000 --rpc.allow-unprotected-txs --history.transactions 0 --tries-verify-mode none
|
||||
|
||||
@@ -183,7 +181,7 @@ This tool is optional and if you leave it out you can always attach to an alread
|
||||
|
||||
#### 7. More
|
||||
|
||||
More details about [running a node](https://docs.bnbchain.org/docs/validator/fullnode) and [becoming a validator](https://docs.bnbchain.org/docs/validator/create-val)
|
||||
More details about [running a node](https://docs.bnbchain.org/bnb-smart-chain/developers/node_operators/full_node/) and [becoming a validator](https://docs.bnbchain.org/bnb-smart-chain/validator/create-val/)
|
||||
|
||||
*Note: Although some internal protective measures prevent transactions from
|
||||
crossing over between the main network and test network, you should always
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
)
|
||||
|
||||
// The ABI holds information about a contract's context and available
|
||||
// invokable methods. It will allow you to type check function calls and
|
||||
// invocable methods. It will allow you to type check function calls and
|
||||
// packs data accordingly.
|
||||
type ABI struct {
|
||||
Constructor Method
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethclient/simulated"
|
||||
)
|
||||
|
||||
@@ -43,7 +43,7 @@ func (b *SimulatedBackend) Fork(ctx context.Context, parentHash common.Hash) err
|
||||
//
|
||||
// Deprecated: please use simulated.Backend from package
|
||||
// github.com/ethereum/go-ethereum/ethclient/simulated instead.
|
||||
func NewSimulatedBackend(alloc core.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||
func NewSimulatedBackend(alloc types.GenesisAlloc, gasLimit uint64) *SimulatedBackend {
|
||||
b := simulated.NewBackend(alloc, simulated.WithBlockGasLimit(gasLimit))
|
||||
return &SimulatedBackend{
|
||||
Backend: b,
|
||||
|
||||
@@ -289,7 +289,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -297,7 +297,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy an interaction tester contract and call a transaction on it
|
||||
@@ -345,7 +345,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -353,7 +353,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy a tuple tester contract and execute a structured call on it
|
||||
@@ -391,7 +391,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -399,7 +399,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy a tuple tester contract and execute a structured call on it
|
||||
@@ -449,7 +449,7 @@ var bindTests = []struct {
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -457,7 +457,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy a slice tester contract and execute a n array call on it
|
||||
@@ -497,7 +497,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -505,7 +505,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy a default method invoker contract and execute its default method
|
||||
@@ -564,7 +564,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -572,7 +572,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy a structs method invoker contract and execute its default method
|
||||
@@ -610,12 +610,12 @@ var bindTests = []struct {
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
`,
|
||||
`
|
||||
// Create a simulator and wrap a non-deployed contract
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000))
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{}, uint64(10000000000))
|
||||
defer sim.Close()
|
||||
|
||||
nonexistent, err := NewNonExistent(common.Address{}, sim)
|
||||
@@ -649,12 +649,12 @@ var bindTests = []struct {
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
`,
|
||||
`
|
||||
// Create a simulator and wrap a non-deployed contract
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{}, uint64(10000000000))
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{}, uint64(10000000000))
|
||||
defer sim.Close()
|
||||
|
||||
nonexistent, err := NewNonExistentStruct(common.Address{}, sim)
|
||||
@@ -696,7 +696,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -704,7 +704,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy a funky gas pattern contract
|
||||
@@ -746,7 +746,7 @@ var bindTests = []struct {
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -754,7 +754,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy a sender tester contract and execute a structured call on it
|
||||
@@ -821,7 +821,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -829,7 +829,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy a underscorer tester contract and execute a structured call on it
|
||||
@@ -915,7 +915,7 @@ var bindTests = []struct {
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -923,7 +923,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy an eventer contract
|
||||
@@ -1105,7 +1105,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -1113,7 +1113,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
//deploy the test contract
|
||||
@@ -1240,7 +1240,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
|
||||
@@ -1248,7 +1248,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
_, _, contract, err := DeployTuple(auth, sim)
|
||||
@@ -1382,7 +1382,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -1390,7 +1390,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
//deploy the test contract
|
||||
@@ -1448,14 +1448,14 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
// Initialize test accounts
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// deploy the test contract
|
||||
@@ -1537,7 +1537,7 @@ var bindTests = []struct {
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
`,
|
||||
`
|
||||
// Initialize test accounts
|
||||
@@ -1545,7 +1545,7 @@ var bindTests = []struct {
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
// Deploy registrar contract
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
@@ -1600,14 +1600,14 @@ var bindTests = []struct {
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
`,
|
||||
`
|
||||
key, _ := crypto.GenerateKey()
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
// Deploy registrar contract
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
transactOpts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
@@ -1661,7 +1661,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
@@ -1669,7 +1669,7 @@ var bindTests = []struct {
|
||||
key, _ := crypto.GenerateKey()
|
||||
auth, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{auth.From: {Balance: big.NewInt(10000000000000000)}}, 10000000)
|
||||
defer sim.Close()
|
||||
|
||||
// Deploy a tester contract and execute a structured call on it
|
||||
@@ -1722,14 +1722,14 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
`,
|
||||
`
|
||||
key, _ := crypto.GenerateKey()
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
sim := backends.NewSimulatedBackend(core.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 1000000)
|
||||
sim := backends.NewSimulatedBackend(types.GenesisAlloc{addr: {Balance: big.NewInt(10000000000000000)}}, 1000000)
|
||||
defer sim.Close()
|
||||
|
||||
opts, _ := bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
@@ -1810,7 +1810,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
`,
|
||||
@@ -1818,7 +1818,7 @@ var bindTests = []struct {
|
||||
var (
|
||||
key, _ = crypto.GenerateKey()
|
||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
)
|
||||
defer sim.Close()
|
||||
|
||||
@@ -1881,7 +1881,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
`,
|
||||
@@ -1889,7 +1889,7 @@ var bindTests = []struct {
|
||||
var (
|
||||
key, _ = crypto.GenerateKey()
|
||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
)
|
||||
defer sim.Close()
|
||||
|
||||
@@ -1934,7 +1934,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
`,
|
||||
@@ -1942,7 +1942,7 @@ var bindTests = []struct {
|
||||
var (
|
||||
key, _ = crypto.GenerateKey()
|
||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
)
|
||||
defer sim.Close()
|
||||
|
||||
@@ -1983,7 +1983,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
`,
|
||||
@@ -1991,7 +1991,7 @@ var bindTests = []struct {
|
||||
var (
|
||||
key, _ = crypto.GenerateKey()
|
||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
)
|
||||
defer sim.Close()
|
||||
|
||||
@@ -2024,7 +2024,7 @@ var bindTests = []struct {
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
`,
|
||||
@@ -2032,7 +2032,7 @@ var bindTests = []struct {
|
||||
var (
|
||||
key, _ = crypto.GenerateKey()
|
||||
user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
|
||||
sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
sim = backends.NewSimulatedBackend(types.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
|
||||
)
|
||||
_, tx, _, err := DeployRangeKeyword(user, sim)
|
||||
if err != nil {
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient/simulated"
|
||||
@@ -57,7 +56,7 @@ func TestWaitDeployed(t *testing.T) {
|
||||
t.Parallel()
|
||||
for name, test := range waitDeployedTests {
|
||||
backend := simulated.NewBackend(
|
||||
core.GenesisAlloc{
|
||||
types.GenesisAlloc{
|
||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
||||
},
|
||||
)
|
||||
@@ -65,7 +64,7 @@ func TestWaitDeployed(t *testing.T) {
|
||||
|
||||
// Create the transaction
|
||||
head, _ := backend.Client().HeaderByNumber(context.Background(), nil) // Should be child's, good enough
|
||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(1))
|
||||
gasPrice := new(big.Int).Add(head.BaseFee, big.NewInt(params.GWei))
|
||||
|
||||
tx := types.NewContractCreation(0, big.NewInt(0), test.gas, gasPrice, common.FromHex(test.code))
|
||||
tx, _ = types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(1337)), testKey)
|
||||
@@ -102,7 +101,7 @@ func TestWaitDeployed(t *testing.T) {
|
||||
|
||||
func TestWaitDeployedCornerCases(t *testing.T) {
|
||||
backend := simulated.NewBackend(
|
||||
core.GenesisAlloc{
|
||||
types.GenesisAlloc{
|
||||
crypto.PubkeyToAddress(testKey.PublicKey): {Balance: big.NewInt(10000000000000000)},
|
||||
},
|
||||
)
|
||||
|
||||
2
accounts/external/backend.go
vendored
2
accounts/external/backend.go
vendored
@@ -163,7 +163,7 @@ func (api *ExternalSigner) SignData(account accounts.Account, mimeType string, d
|
||||
hexutil.Encode(data)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// If V is on 27/28-form, convert to to 0/1 for Clique and Parlia
|
||||
// If V is on 27/28-form, convert to 0/1 for Clique and Parlia
|
||||
if (mimeType == accounts.MimetypeClique || mimeType == accounts.MimetypeParlia) && (res[64] == 27 || res[64] == 28) {
|
||||
res[64] -= 27 // Transform V from 27/28 to 0/1 for Clique and Parlia use
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// waitWatcherStarts waits up to 1s for the keystore watcher to start.
|
||||
// waitWatcherStart waits up to 1s for the keystore watcher to start.
|
||||
func waitWatcherStart(ks *KeyStore) bool {
|
||||
// On systems where file watch is not supported, just return "ok".
|
||||
if !ks.cache.watcher.enabled() {
|
||||
@@ -358,7 +358,6 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
// Now replace file contents
|
||||
if err := forceCopyFile(file, cachetestAccounts[1].URL.Path); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
wantAccounts = []accounts.Account{cachetestAccounts[1]}
|
||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||
@@ -374,7 +373,6 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
// Now replace file contents again
|
||||
if err := forceCopyFile(file, cachetestAccounts[2].URL.Path); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
wantAccounts = []accounts.Account{cachetestAccounts[2]}
|
||||
wantAccounts[0].URL = accounts.URL{Scheme: KeyStoreScheme, Path: file}
|
||||
@@ -390,7 +388,6 @@ func TestUpdatedKeyfileContents(t *testing.T) {
|
||||
// Now replace file contents with crap
|
||||
if err := os.WriteFile(file, []byte("foo"), 0600); err != nil {
|
||||
t.Fatal(err)
|
||||
return
|
||||
}
|
||||
if err := waitForAccounts([]accounts.Account{}, ks); err != nil {
|
||||
t.Errorf("Emptying account file failed")
|
||||
|
||||
@@ -343,7 +343,7 @@ func TestWalletNotifications(t *testing.T) {
|
||||
checkEvents(t, wantEvents, events)
|
||||
}
|
||||
|
||||
// TestImportExport tests the import functionality of a keystore.
|
||||
// TestImportECDSA tests the import functionality of a keystore.
|
||||
func TestImportECDSA(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
@@ -362,7 +362,7 @@ func TestImportECDSA(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestImportECDSA tests the import and export functionality of a keystore.
|
||||
// TestImportExport tests the import and export functionality of a keystore.
|
||||
func TestImportExport(t *testing.T) {
|
||||
t.Parallel()
|
||||
_, ks := tmpKeyStore(t, true)
|
||||
|
||||
@@ -241,7 +241,7 @@ func (hub *Hub) refreshWallets() {
|
||||
card.Disconnect(pcsc.LeaveCard)
|
||||
continue
|
||||
}
|
||||
// Card connected, start tracking in amongs the wallets
|
||||
// Card connected, start tracking among the wallets
|
||||
hub.wallets[reader] = wallet
|
||||
events = append(events, accounts.WalletEvent{Wallet: wallet, Kind: accounts.WalletArrived})
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
|
||||
// This file contains the implementation for interacting with the Trezor hardware
|
||||
// wallets. The wire protocol spec can be found on the SatoshiLabs website:
|
||||
// https://wiki.trezor.io/Developers_guide-Message_Workflows
|
||||
// https://docs.trezor.io/trezor-firmware/common/message-workflows.html
|
||||
|
||||
// !!! STAHP !!!
|
||||
//
|
||||
|
||||
@@ -263,7 +263,7 @@ func ExecutableDataToBlock(params ExecutableData, versionedHashes []common.Hash,
|
||||
|
||||
// BlockToExecutableData constructs the ExecutableData structure by filling the
|
||||
// fields from the given block. It assumes the given block is post-merge block.
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars []*types.BlobTxSidecar) *ExecutionPayloadEnvelope {
|
||||
func BlockToExecutableData(block *types.Block, fees *big.Int, sidecars types.BlobSidecars) *ExecutionPayloadEnvelope {
|
||||
data := &ExecutableData{
|
||||
BlockHash: block.Hash(),
|
||||
ParentHash: block.ParentHash(),
|
||||
@@ -303,3 +303,21 @@ type ExecutionPayloadBodyV1 struct {
|
||||
TransactionData []hexutil.Bytes `json:"transactions"`
|
||||
Withdrawals []*types.Withdrawal `json:"withdrawals"`
|
||||
}
|
||||
|
||||
// Client identifiers to support ClientVersionV1.
|
||||
const (
|
||||
ClientCode = "GE"
|
||||
ClientName = "go-ethereum"
|
||||
)
|
||||
|
||||
// ClientVersionV1 contains information which identifies a client implementation.
|
||||
type ClientVersionV1 struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"clientName"`
|
||||
Version string `json:"version"`
|
||||
Commit string `json:"commit"`
|
||||
}
|
||||
|
||||
func (v *ClientVersionV1) String() string {
|
||||
return fmt.Sprintf("%s-%s-%s-%s", v.Code, v.Name, v.Version, v.Commit)
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
# This file contains sha256 checksums of optional build dependencies.
|
||||
|
||||
# version:spec-tests 1.0.6
|
||||
# version:spec-tests 2.1.0
|
||||
# https://github.com/ethereum/execution-spec-tests/releases
|
||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v1.0.6/
|
||||
485af7b66cf41eb3a8c1bd46632913b8eb95995df867cf665617bbc9b4beedd1 fixtures_develop.tar.gz
|
||||
# https://github.com/ethereum/execution-spec-tests/releases/download/v2.1.0/
|
||||
ca89c76851b0900bfcc3cbb9a26cbece1f3d7c64a3bed38723e914713290df6c fixtures_develop.tar.gz
|
||||
|
||||
# version:golang 1.21.6
|
||||
# https://go.dev/dl/
|
||||
|
||||
@@ -121,14 +121,13 @@ var (
|
||||
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
||||
// Note: the following Ubuntu releases have been officially deprecated on Launchpad:
|
||||
// wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy, hirsuite, impish,
|
||||
// kinetic
|
||||
// kinetic, lunar
|
||||
debDistroGoBoots = map[string]string{
|
||||
"trusty": "golang-1.11", // 14.04, EOL: 04/2024
|
||||
"xenial": "golang-go", // 16.04, EOL: 04/2026
|
||||
"bionic": "golang-go", // 18.04, EOL: 04/2028
|
||||
"focal": "golang-go", // 20.04, EOL: 04/2030
|
||||
"jammy": "golang-go", // 22.04, EOL: 04/2032
|
||||
"lunar": "golang-go", // 23.04, EOL: 01/2024
|
||||
"mantic": "golang-go", // 23.10, EOL: 07/2024
|
||||
}
|
||||
|
||||
|
||||
@@ -916,7 +916,7 @@ There are a couple of implementation for a UI. We'll try to keep this list up to
|
||||
|
||||
| Name | Repo | UI type| No external resources| Blocky support| Verifies permissions | Hash information | No secondary storage | Statically linked| Can modify parameters|
|
||||
| ---- | ---- | -------| ---- | ---- | ---- |---- | ---- | ---- | ---- |
|
||||
| QtSigner| https://github.com/holiman/qtsigner/| Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)|
|
||||
| GtkSigner| https://github.com/holiman/gtksigner| Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: |
|
||||
| Frame | https://github.com/floating/frame/commits/go-signer| Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: |
|
||||
| Clef UI| https://github.com/ethereum/clef-ui| Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: | :+1: (approve tx only)|
|
||||
| QtSigner| https://github.com/holiman/qtsigner/ | Python3/QT-based| :+1:| :+1:| :+1:| :+1:| :+1:| :x: | :+1: (partially)|
|
||||
| GtkSigner| https://github.com/holiman/gtksigner | Python3/GTK-based| :+1:| :x:| :x:| :+1:| :+1:| :x: | :x: |
|
||||
| Frame | https://github.com/floating/frame/commits/go-signer | Electron-based| :x:| :x:| :x:| :x:| ?| :x: | :x: |
|
||||
| Clef UI| https://github.com/ethereum/clef-ui | Golang/QT-based| :+1:| :+1:| :x:| :+1:| :+1:| :x: | :+1: (approve tx only)|
|
||||
|
||||
@@ -75,7 +75,7 @@ Example:
|
||||
},
|
||||
{
|
||||
"type": "Info",
|
||||
"message": "User should see this aswell"
|
||||
"message": "User should see this as well"
|
||||
}
|
||||
],
|
||||
"meta": {
|
||||
|
||||
@@ -166,7 +166,7 @@ func (c *Conn) ReadEth() (any, error) {
|
||||
case eth.TransactionsMsg:
|
||||
msg = new(eth.TransactionsPacket)
|
||||
case eth.NewPooledTransactionHashesMsg:
|
||||
msg = new(eth.NewPooledTransactionHashesPacket68)
|
||||
msg = new(eth.NewPooledTransactionHashesPacket)
|
||||
case eth.GetPooledTransactionsMsg:
|
||||
msg = new(eth.GetPooledTransactionsPacket)
|
||||
case eth.PooledTransactionsMsg:
|
||||
|
||||
@@ -64,23 +64,23 @@ func NewSuite(dest *enode.Node, chainDir, engineURL, jwt string) (*Suite, error)
|
||||
func (s *Suite) EthTests() []utesting.Test {
|
||||
return []utesting.Test{
|
||||
// status
|
||||
{Name: "TestStatus", Fn: s.TestStatus},
|
||||
{Name: "Status", Fn: s.TestStatus},
|
||||
// get block headers
|
||||
{Name: "TestGetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
||||
{Name: "TestSimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
||||
{Name: "TestSameRequestID", Fn: s.TestSameRequestID},
|
||||
{Name: "TestZeroRequestID", Fn: s.TestZeroRequestID},
|
||||
{Name: "GetBlockHeaders", Fn: s.TestGetBlockHeaders},
|
||||
{Name: "SimultaneousRequests", Fn: s.TestSimultaneousRequests},
|
||||
{Name: "SameRequestID", Fn: s.TestSameRequestID},
|
||||
{Name: "ZeroRequestID", Fn: s.TestZeroRequestID},
|
||||
// get block bodies
|
||||
{Name: "TestGetBlockBodies", Fn: s.TestGetBlockBodies},
|
||||
{Name: "GetBlockBodies", Fn: s.TestGetBlockBodies},
|
||||
// // malicious handshakes + status
|
||||
{Name: "TestMaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
||||
{Name: "TestMaliciousStatus", Fn: s.TestMaliciousStatus},
|
||||
{Name: "MaliciousHandshake", Fn: s.TestMaliciousHandshake},
|
||||
{Name: "MaliciousStatus", Fn: s.TestMaliciousStatus},
|
||||
// test transactions
|
||||
{Name: "TestLargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
|
||||
{Name: "TestTransaction", Fn: s.TestTransaction},
|
||||
{Name: "TestInvalidTxs", Fn: s.TestInvalidTxs},
|
||||
{Name: "TestNewPooledTxs", Fn: s.TestNewPooledTxs},
|
||||
{Name: "TestBlobViolations", Fn: s.TestBlobViolations},
|
||||
{Name: "LargeTxRequest", Fn: s.TestLargeTxRequest, Slow: true},
|
||||
{Name: "Transaction", Fn: s.TestTransaction},
|
||||
{Name: "InvalidTxs", Fn: s.TestInvalidTxs},
|
||||
{Name: "NewPooledTxs", Fn: s.TestNewPooledTxs},
|
||||
{Name: "BlobViolations", Fn: s.TestBlobViolations},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,9 +94,9 @@ func (s *Suite) SnapTests() []utesting.Test {
|
||||
}
|
||||
}
|
||||
|
||||
// TestStatus attempts to connect to the given node and exchange a status
|
||||
// message with it on the eth protocol.
|
||||
func (s *Suite) TestStatus(t *utesting.T) {
|
||||
t.Log(`This test is just a sanity check. It performs an eth protocol handshake.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
@@ -112,9 +112,9 @@ func headersMatch(expected []*types.Header, headers []*types.Header) bool {
|
||||
return reflect.DeepEqual(expected, headers)
|
||||
}
|
||||
|
||||
// TestGetBlockHeaders tests whether the given node can respond to an eth
|
||||
// `GetBlockHeaders` request and that the response is accurate.
|
||||
func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
||||
t.Log(`This test requests block headers from the node.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
@@ -154,10 +154,10 @@ func (s *Suite) TestGetBlockHeaders(t *utesting.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSimultaneousRequests sends two simultaneous `GetBlockHeader` requests
|
||||
// from the same connection with different request IDs and checks to make sure
|
||||
// the node responds with the correct headers per request.
|
||||
func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
|
||||
t.Log(`This test requests blocks headers from the node, performing two requests
|
||||
concurrently, with different request IDs.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
@@ -228,9 +228,10 @@ func (s *Suite) TestSimultaneousRequests(t *utesting.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestSameRequestID sends two requests with the same request ID to a single
|
||||
// node.
|
||||
func (s *Suite) TestSameRequestID(t *utesting.T) {
|
||||
t.Log(`This test requests block headers, performing two concurrent requests with the
|
||||
same request ID. The node should handle the request by responding to both requests.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
@@ -298,9 +299,10 @@ func (s *Suite) TestSameRequestID(t *utesting.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestZeroRequestID checks that a message with a request ID of zero is still handled
|
||||
// by the node.
|
||||
func (s *Suite) TestZeroRequestID(t *utesting.T) {
|
||||
t.Log(`This test sends a GetBlockHeaders message with a request-id of zero,
|
||||
and expects a response.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
@@ -333,9 +335,9 @@ func (s *Suite) TestZeroRequestID(t *utesting.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetBlockBodies tests whether the given node can respond to a
|
||||
// `GetBlockBodies` request and that the response is accurate.
|
||||
func (s *Suite) TestGetBlockBodies(t *utesting.T) {
|
||||
t.Log(`This test sends GetBlockBodies requests to the node for known blocks in the test chain.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
@@ -376,12 +378,12 @@ func randBuf(size int) []byte {
|
||||
return buf
|
||||
}
|
||||
|
||||
// TestMaliciousHandshake tries to send malicious data during the handshake.
|
||||
func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
|
||||
key, _ := crypto.GenerateKey()
|
||||
t.Log(`This test tries to send malicious data during the devp2p handshake, in various ways.`)
|
||||
|
||||
// Write hello to client.
|
||||
var (
|
||||
key, _ = crypto.GenerateKey()
|
||||
pub0 = crypto.FromECDSAPub(&key.PublicKey)[1:]
|
||||
version = eth.ProtocolVersions[0]
|
||||
)
|
||||
@@ -451,8 +453,9 @@ func (s *Suite) TestMaliciousHandshake(t *utesting.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestMaliciousStatus sends a status package with a large total difficulty.
|
||||
func (s *Suite) TestMaliciousStatus(t *utesting.T) {
|
||||
t.Log(`This test sends a malicious eth Status message to the node and expects a disconnect.`)
|
||||
|
||||
conn, err := s.dial()
|
||||
if err != nil {
|
||||
t.Fatalf("dial failed: %v", err)
|
||||
@@ -486,9 +489,10 @@ func (s *Suite) TestMaliciousStatus(t *utesting.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestTransaction sends a valid transaction to the node and checks if the
|
||||
// transaction gets propagated.
|
||||
func (s *Suite) TestTransaction(t *utesting.T) {
|
||||
t.Log(`This test sends a valid transaction to the node and checks if the
|
||||
transaction gets propagated.`)
|
||||
|
||||
// Nudge client out of syncing mode to accept pending txs.
|
||||
if err := s.engine.sendForkchoiceUpdated(); err != nil {
|
||||
t.Fatalf("failed to send next block: %v", err)
|
||||
@@ -507,15 +511,16 @@ func (s *Suite) TestTransaction(t *utesting.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to sign tx: %v", err)
|
||||
}
|
||||
if err := s.sendTxs([]*types.Transaction{tx}); err != nil {
|
||||
if err := s.sendTxs(t, []*types.Transaction{tx}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
s.chain.IncNonce(from, 1)
|
||||
}
|
||||
|
||||
// TestInvalidTxs sends several invalid transactions and tests whether
|
||||
// the node will propagate them.
|
||||
func (s *Suite) TestInvalidTxs(t *utesting.T) {
|
||||
t.Log(`This test sends several kinds of invalid transactions and checks that the node
|
||||
does not propagate them.`)
|
||||
|
||||
// Nudge client out of syncing mode to accept pending txs.
|
||||
if err := s.engine.sendForkchoiceUpdated(); err != nil {
|
||||
t.Fatalf("failed to send next block: %v", err)
|
||||
@@ -534,7 +539,7 @@ func (s *Suite) TestInvalidTxs(t *utesting.T) {
|
||||
if err != nil {
|
||||
t.Fatalf("failed to sign tx: %v", err)
|
||||
}
|
||||
if err := s.sendTxs([]*types.Transaction{tx}); err != nil {
|
||||
if err := s.sendTxs(t, []*types.Transaction{tx}); err != nil {
|
||||
t.Fatalf("failed to send txs: %v", err)
|
||||
}
|
||||
s.chain.IncNonce(from, 1)
|
||||
@@ -590,14 +595,15 @@ func (s *Suite) TestInvalidTxs(t *utesting.T) {
|
||||
}
|
||||
txs = append(txs, tx)
|
||||
}
|
||||
if err := s.sendInvalidTxs(txs); err != nil {
|
||||
if err := s.sendInvalidTxs(t, txs); err != nil {
|
||||
t.Fatalf("failed to send invalid txs: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestLargeTxRequest tests whether a node can fulfill a large GetPooledTransactions
|
||||
// request.
|
||||
func (s *Suite) TestLargeTxRequest(t *utesting.T) {
|
||||
t.Log(`This test first send ~2000 transactions to the node, then requests them
|
||||
on another peer connection using GetPooledTransactions.`)
|
||||
|
||||
// Nudge client out of syncing mode to accept pending txs.
|
||||
if err := s.engine.sendForkchoiceUpdated(); err != nil {
|
||||
t.Fatalf("failed to send next block: %v", err)
|
||||
@@ -630,7 +636,7 @@ func (s *Suite) TestLargeTxRequest(t *utesting.T) {
|
||||
s.chain.IncNonce(from, uint64(count))
|
||||
|
||||
// Send txs.
|
||||
if err := s.sendTxs(txs); err != nil {
|
||||
if err := s.sendTxs(t, txs); err != nil {
|
||||
t.Fatalf("failed to send txs: %v", err)
|
||||
}
|
||||
|
||||
@@ -667,13 +673,15 @@ func (s *Suite) TestLargeTxRequest(t *utesting.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestNewPooledTxs tests whether a node will do a GetPooledTransactions request
|
||||
// upon receiving a NewPooledTransactionHashes announcement.
|
||||
func (s *Suite) TestNewPooledTxs(t *utesting.T) {
|
||||
t.Log(`This test announces transaction hashes to the node and expects it to fetch
|
||||
the transactions using a GetPooledTransactions request.`)
|
||||
|
||||
// Nudge client out of syncing mode to accept pending txs.
|
||||
if err := s.engine.sendForkchoiceUpdated(); err != nil {
|
||||
t.Fatalf("failed to send next block: %v", err)
|
||||
}
|
||||
|
||||
var (
|
||||
count = 50
|
||||
from, nonce = s.chain.GetSender(1)
|
||||
@@ -710,7 +718,7 @@ func (s *Suite) TestNewPooledTxs(t *utesting.T) {
|
||||
}
|
||||
|
||||
// Send announcement.
|
||||
ann := eth.NewPooledTransactionHashesPacket68{Types: txTypes, Sizes: sizes, Hashes: hashes}
|
||||
ann := eth.NewPooledTransactionHashesPacket{Types: txTypes, Sizes: sizes, Hashes: hashes}
|
||||
err = conn.Write(ethProto, eth.NewPooledTransactionHashesMsg, ann)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to write to connection: %v", err)
|
||||
@@ -728,7 +736,7 @@ func (s *Suite) TestNewPooledTxs(t *utesting.T) {
|
||||
t.Fatalf("unexpected number of txs requested: wanted %d, got %d", len(hashes), len(msg.GetPooledTransactionsRequest))
|
||||
}
|
||||
return
|
||||
case *eth.NewPooledTransactionHashesPacket68:
|
||||
case *eth.NewPooledTransactionHashesPacket:
|
||||
continue
|
||||
case *eth.TransactionsPacket:
|
||||
continue
|
||||
@@ -762,7 +770,7 @@ func (s *Suite) makeBlobTxs(count, blobs int, discriminator byte) (txs types.Tra
|
||||
from, nonce := s.chain.GetSender(5)
|
||||
for i := 0; i < count; i++ {
|
||||
// Make blob data, max of 2 blobs per tx.
|
||||
blobdata := make([]byte, blobs%2)
|
||||
blobdata := make([]byte, blobs%3)
|
||||
for i := range blobdata {
|
||||
blobdata[i] = discriminator
|
||||
blobs -= 1
|
||||
@@ -787,6 +795,8 @@ func (s *Suite) makeBlobTxs(count, blobs int, discriminator byte) (txs types.Tra
|
||||
}
|
||||
|
||||
func (s *Suite) TestBlobViolations(t *utesting.T) {
|
||||
t.Log(`This test sends some invalid blob tx announcements and expects the node to disconnect.`)
|
||||
|
||||
if err := s.engine.sendForkchoiceUpdated(); err != nil {
|
||||
t.Fatalf("send fcu failed: %v", err)
|
||||
}
|
||||
@@ -796,12 +806,12 @@ func (s *Suite) TestBlobViolations(t *utesting.T) {
|
||||
t2 = s.makeBlobTxs(2, 3, 0x2)
|
||||
)
|
||||
for _, test := range []struct {
|
||||
ann eth.NewPooledTransactionHashesPacket68
|
||||
ann eth.NewPooledTransactionHashesPacket
|
||||
resp eth.PooledTransactionsResponse
|
||||
}{
|
||||
// Invalid tx size.
|
||||
{
|
||||
ann: eth.NewPooledTransactionHashesPacket68{
|
||||
ann: eth.NewPooledTransactionHashesPacket{
|
||||
Types: []byte{types.BlobTxType, types.BlobTxType},
|
||||
Sizes: []uint32{uint32(t1[0].Size()), uint32(t1[1].Size() + 10)},
|
||||
Hashes: []common.Hash{t1[0].Hash(), t1[1].Hash()},
|
||||
@@ -810,7 +820,7 @@ func (s *Suite) TestBlobViolations(t *utesting.T) {
|
||||
},
|
||||
// Wrong tx type.
|
||||
{
|
||||
ann: eth.NewPooledTransactionHashesPacket68{
|
||||
ann: eth.NewPooledTransactionHashesPacket{
|
||||
Types: []byte{types.DynamicFeeTxType, types.BlobTxType},
|
||||
Sizes: []uint32{uint32(t2[0].Size()), uint32(t2[1].Size())},
|
||||
Hashes: []common.Hash{t2[0].Hash(), t2[1].Hash()},
|
||||
|
||||
@@ -25,11 +25,12 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/eth"
|
||||
"github.com/ethereum/go-ethereum/internal/utesting"
|
||||
)
|
||||
|
||||
// sendTxs sends the given transactions to the node and
|
||||
// expects the node to accept and propagate them.
|
||||
func (s *Suite) sendTxs(txs []*types.Transaction) error {
|
||||
func (s *Suite) sendTxs(t *utesting.T, txs []*types.Transaction) error {
|
||||
// Open sending conn.
|
||||
sendConn, err := s.dial()
|
||||
if err != nil {
|
||||
@@ -70,10 +71,19 @@ func (s *Suite) sendTxs(txs []*types.Transaction) error {
|
||||
for _, tx := range *msg {
|
||||
got[tx.Hash()] = true
|
||||
}
|
||||
case *eth.NewPooledTransactionHashesPacket68:
|
||||
case *eth.NewPooledTransactionHashesPacket:
|
||||
for _, hash := range msg.Hashes {
|
||||
got[hash] = true
|
||||
}
|
||||
case *eth.GetBlockHeadersPacket:
|
||||
headers, err := s.chain.GetHeaders(msg)
|
||||
if err != nil {
|
||||
t.Logf("invalid GetBlockHeaders request: %v", err)
|
||||
}
|
||||
recvConn.Write(ethProto, eth.BlockHeadersMsg, ð.BlockHeadersPacket{
|
||||
RequestId: msg.RequestId,
|
||||
BlockHeadersRequest: headers,
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("unexpected eth wire msg: %s", pretty.Sdump(msg))
|
||||
}
|
||||
@@ -95,7 +105,7 @@ func (s *Suite) sendTxs(txs []*types.Transaction) error {
|
||||
return fmt.Errorf("timed out waiting for txs")
|
||||
}
|
||||
|
||||
func (s *Suite) sendInvalidTxs(txs []*types.Transaction) error {
|
||||
func (s *Suite) sendInvalidTxs(t *utesting.T, txs []*types.Transaction) error {
|
||||
// Open sending conn.
|
||||
sendConn, err := s.dial()
|
||||
if err != nil {
|
||||
@@ -146,12 +156,21 @@ func (s *Suite) sendInvalidTxs(txs []*types.Transaction) error {
|
||||
return fmt.Errorf("received bad tx: %s", tx.Hash())
|
||||
}
|
||||
}
|
||||
case *eth.NewPooledTransactionHashesPacket68:
|
||||
case *eth.NewPooledTransactionHashesPacket:
|
||||
for _, hash := range msg.Hashes {
|
||||
if _, ok := invalids[hash]; ok {
|
||||
return fmt.Errorf("received bad tx: %s", hash)
|
||||
}
|
||||
}
|
||||
case *eth.GetBlockHeadersPacket:
|
||||
headers, err := s.chain.GetHeaders(msg)
|
||||
if err != nil {
|
||||
t.Logf("invalid GetBlockHeaders request: %v", err)
|
||||
}
|
||||
recvConn.Write(ethProto, eth.BlockHeadersMsg, ð.BlockHeadersPacket{
|
||||
RequestId: msg.RequestId,
|
||||
BlockHeadersRequest: headers,
|
||||
})
|
||||
default:
|
||||
return fmt.Errorf("unexpected eth message: %v", pretty.Sdump(msg))
|
||||
}
|
||||
|
||||
324
cmd/era/main.go
Normal file
324
cmd/era/main.go
Normal file
@@ -0,0 +1,324 @@
|
||||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/internal/era"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
var app = flags.NewApp("go-ethereum era tool")
|
||||
|
||||
var (
|
||||
dirFlag = &cli.StringFlag{
|
||||
Name: "dir",
|
||||
Usage: "directory storing all relevant era1 files",
|
||||
Value: "eras",
|
||||
}
|
||||
networkFlag = &cli.StringFlag{
|
||||
Name: "network",
|
||||
Usage: "network name associated with era1 files",
|
||||
Value: "mainnet",
|
||||
}
|
||||
eraSizeFlag = &cli.IntFlag{
|
||||
Name: "size",
|
||||
Usage: "number of blocks per era",
|
||||
Value: era.MaxEra1Size,
|
||||
}
|
||||
txsFlag = &cli.BoolFlag{
|
||||
Name: "txs",
|
||||
Usage: "print full transaction values",
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
blockCommand = &cli.Command{
|
||||
Name: "block",
|
||||
Usage: "get block data",
|
||||
ArgsUsage: "<number>",
|
||||
Action: block,
|
||||
Flags: []cli.Flag{
|
||||
txsFlag,
|
||||
},
|
||||
}
|
||||
infoCommand = &cli.Command{
|
||||
Name: "info",
|
||||
ArgsUsage: "<epoch>",
|
||||
Usage: "get epoch information",
|
||||
Action: info,
|
||||
}
|
||||
verifyCommand = &cli.Command{
|
||||
Name: "verify",
|
||||
ArgsUsage: "<expected>",
|
||||
Usage: "verifies each era1 against expected accumulator root",
|
||||
Action: verify,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
app.Commands = []*cli.Command{
|
||||
blockCommand,
|
||||
infoCommand,
|
||||
verifyCommand,
|
||||
}
|
||||
app.Flags = []cli.Flag{
|
||||
dirFlag,
|
||||
networkFlag,
|
||||
eraSizeFlag,
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// block prints the specified block from an era1 store.
|
||||
func block(ctx *cli.Context) error {
|
||||
num, err := strconv.ParseUint(ctx.Args().First(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid block number: %w", err)
|
||||
}
|
||||
e, err := open(ctx, num/uint64(ctx.Int(eraSizeFlag.Name)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening era1: %w", err)
|
||||
}
|
||||
defer e.Close()
|
||||
// Read block with number.
|
||||
block, err := e.GetBlockByNumber(num)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading block %d: %w", num, err)
|
||||
}
|
||||
// Convert block to JSON and print.
|
||||
val := ethapi.RPCMarshalBlock(block, ctx.Bool(txsFlag.Name), ctx.Bool(txsFlag.Name), params.MainnetChainConfig)
|
||||
b, err := json.MarshalIndent(val, "", " ")
|
||||
if err != nil {
|
||||
return fmt.Errorf("error marshaling json: %w", err)
|
||||
}
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
// info prints some high-level information about the era1 file.
|
||||
func info(ctx *cli.Context) error {
|
||||
epoch, err := strconv.ParseUint(ctx.Args().First(), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid epoch number: %w", err)
|
||||
}
|
||||
e, err := open(ctx, epoch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer e.Close()
|
||||
acc, err := e.Accumulator()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading accumulator: %w", err)
|
||||
}
|
||||
td, err := e.InitialTD()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading total difficulty: %w", err)
|
||||
}
|
||||
info := struct {
|
||||
Accumulator common.Hash `json:"accumulator"`
|
||||
TotalDifficulty *big.Int `json:"totalDifficulty"`
|
||||
StartBlock uint64 `json:"startBlock"`
|
||||
Count uint64 `json:"count"`
|
||||
}{
|
||||
acc, td, e.Start(), e.Count(),
|
||||
}
|
||||
b, _ := json.MarshalIndent(info, "", " ")
|
||||
fmt.Println(string(b))
|
||||
return nil
|
||||
}
|
||||
|
||||
// open opens an era1 file at a certain epoch.
|
||||
func open(ctx *cli.Context, epoch uint64) (*era.Era, error) {
|
||||
var (
|
||||
dir = ctx.String(dirFlag.Name)
|
||||
network = ctx.String(networkFlag.Name)
|
||||
)
|
||||
entries, err := era.ReadDir(dir, network)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading era dir: %w", err)
|
||||
}
|
||||
if epoch >= uint64(len(entries)) {
|
||||
return nil, fmt.Errorf("epoch out-of-bounds: last %d, want %d", len(entries)-1, epoch)
|
||||
}
|
||||
return era.Open(path.Join(dir, entries[epoch]))
|
||||
}
|
||||
|
||||
// verify checks each era1 file in a directory to ensure it is well-formed and
|
||||
// that the accumulator matches the expected value.
|
||||
func verify(ctx *cli.Context) error {
|
||||
if ctx.Args().Len() != 1 {
|
||||
return fmt.Errorf("missing accumulators file")
|
||||
}
|
||||
|
||||
roots, err := readHashes(ctx.Args().First())
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read expected roots file: %w", err)
|
||||
}
|
||||
|
||||
var (
|
||||
dir = ctx.String(dirFlag.Name)
|
||||
network = ctx.String(networkFlag.Name)
|
||||
start = time.Now()
|
||||
reported = time.Now()
|
||||
)
|
||||
|
||||
entries, err := era.ReadDir(dir, network)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading %s: %w", dir, err)
|
||||
}
|
||||
|
||||
if len(entries) != len(roots) {
|
||||
return fmt.Errorf("number of era1 files should match the number of accumulator hashes")
|
||||
}
|
||||
|
||||
// Verify each epoch matches the expected root.
|
||||
for i, want := range roots {
|
||||
// Wrap in function so defers don't stack.
|
||||
err := func() error {
|
||||
name := entries[i]
|
||||
e, err := era.Open(path.Join(dir, name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening era1 file %s: %w", name, err)
|
||||
}
|
||||
defer e.Close()
|
||||
// Read accumulator and check against expected.
|
||||
if got, err := e.Accumulator(); err != nil {
|
||||
return fmt.Errorf("error retrieving accumulator for %s: %w", name, err)
|
||||
} else if got != want {
|
||||
return fmt.Errorf("invalid root %s: got %s, want %s", name, got, want)
|
||||
}
|
||||
// Recompute accumulator.
|
||||
if err := checkAccumulator(e); err != nil {
|
||||
return fmt.Errorf("error verify era1 file %s: %w", name, err)
|
||||
}
|
||||
// Give the user some feedback that something is happening.
|
||||
if time.Since(reported) >= 8*time.Second {
|
||||
fmt.Printf("Verifying Era1 files \t\t verified=%d,\t elapsed=%s\n", i, common.PrettyDuration(time.Since(start)))
|
||||
reported = time.Now()
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// checkAccumulator verifies the accumulator matches the data in the Era.
|
||||
func checkAccumulator(e *era.Era) error {
|
||||
var (
|
||||
err error
|
||||
want common.Hash
|
||||
td *big.Int
|
||||
tds = make([]*big.Int, 0)
|
||||
hashes = make([]common.Hash, 0)
|
||||
)
|
||||
if want, err = e.Accumulator(); err != nil {
|
||||
return fmt.Errorf("error reading accumulator: %w", err)
|
||||
}
|
||||
if td, err = e.InitialTD(); err != nil {
|
||||
return fmt.Errorf("error reading total difficulty: %w", err)
|
||||
}
|
||||
it, err := era.NewIterator(e)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making era iterator: %w", err)
|
||||
}
|
||||
// To fully verify an era the following attributes must be checked:
|
||||
// 1) the block index is constructed correctly
|
||||
// 2) the tx root matches the value in the block
|
||||
// 3) the receipts root matches the value in the block
|
||||
// 4) the starting total difficulty value is correct
|
||||
// 5) the accumulator is correct by recomputing it locally, which verifies
|
||||
// the blocks are all correct (via hash)
|
||||
//
|
||||
// The attributes 1), 2), and 3) are checked for each block. 4) and 5) require
|
||||
// accumulation across the entire set and are verified at the end.
|
||||
for it.Next() {
|
||||
// 1) next() walks the block index, so we're able to implicitly verify it.
|
||||
if it.Error() != nil {
|
||||
return fmt.Errorf("error reading block %d: %w", it.Number(), err)
|
||||
}
|
||||
block, receipts, err := it.BlockAndReceipts()
|
||||
if it.Error() != nil {
|
||||
return fmt.Errorf("error reading block %d: %w", it.Number(), err)
|
||||
}
|
||||
// 2) recompute tx root and verify against header.
|
||||
tr := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil))
|
||||
if tr != block.TxHash() {
|
||||
return fmt.Errorf("tx root in block %d mismatch: want %s, got %s", block.NumberU64(), block.TxHash(), tr)
|
||||
}
|
||||
// 3) recompute receipt root and check value against block.
|
||||
rr := types.DeriveSha(receipts, trie.NewStackTrie(nil))
|
||||
if rr != block.ReceiptHash() {
|
||||
return fmt.Errorf("receipt root in block %d mismatch: want %s, got %s", block.NumberU64(), block.ReceiptHash(), rr)
|
||||
}
|
||||
hashes = append(hashes, block.Hash())
|
||||
td.Add(td, block.Difficulty())
|
||||
tds = append(tds, new(big.Int).Set(td))
|
||||
}
|
||||
// 4+5) Verify accumulator and total difficulty.
|
||||
got, err := era.ComputeAccumulator(hashes, tds)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error computing accumulator: %w", err)
|
||||
}
|
||||
if got != want {
|
||||
return fmt.Errorf("expected accumulator root does not match calculated: got %s, want %s", got, want)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// readHashes reads a file of newline-delimited hashes.
|
||||
func readHashes(f string) ([]common.Hash, error) {
|
||||
b, err := os.ReadFile(f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to open accumulators file")
|
||||
}
|
||||
s := strings.Split(string(b), "\n")
|
||||
// Remove empty last element, if present.
|
||||
if s[len(s)-1] == "" {
|
||||
s = s[:len(s)-1]
|
||||
}
|
||||
// Convert to hashes.
|
||||
r := make([]common.Hash, len(s))
|
||||
for i := range s {
|
||||
r[i] = common.HexToHash(s[i])
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
@@ -36,13 +36,14 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
type Prestate struct {
|
||||
Env stEnv `json:"env"`
|
||||
Pre core.GenesisAlloc `json:"pre"`
|
||||
Env stEnv `json:"env"`
|
||||
Pre types.GenesisAlloc `json:"pre"`
|
||||
}
|
||||
|
||||
// ExecutionResult contains the execution status after running a state test, any
|
||||
@@ -168,7 +169,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
// Calculate the BlobBaseFee
|
||||
var excessBlobGas uint64
|
||||
if pre.Env.ExcessBlobGas != nil {
|
||||
excessBlobGas := *pre.Env.ExcessBlobGas
|
||||
excessBlobGas = *pre.Env.ExcessBlobGas
|
||||
vmContext.BlobBaseFee = eip4844.CalcBlobFee(excessBlobGas)
|
||||
} else {
|
||||
// If it is not explicitly defined, but we have the parent values, we try
|
||||
@@ -356,8 +357,8 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
|
||||
return statedb, execRs, body, nil
|
||||
}
|
||||
|
||||
func MakePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
|
||||
sdb := state.NewDatabaseWithConfig(db, &trie.Config{Preimages: true})
|
||||
func MakePreState(db ethdb.Database, accounts types.GenesisAlloc) *state.StateDB {
|
||||
sdb := state.NewDatabaseWithConfig(db, &triedb.Config{Preimages: true})
|
||||
statedb, _ := state.New(types.EmptyRootHash, sdb, nil)
|
||||
for addr, a := range accounts {
|
||||
statedb.SetCode(addr, a.Code)
|
||||
|
||||
@@ -27,7 +27,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip1559"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
@@ -74,10 +73,10 @@ var (
|
||||
)
|
||||
|
||||
type input struct {
|
||||
Alloc core.GenesisAlloc `json:"alloc,omitempty"`
|
||||
Env *stEnv `json:"env,omitempty"`
|
||||
Txs []*txWithKey `json:"txs,omitempty"`
|
||||
TxRlp string `json:"txsRlp,omitempty"`
|
||||
Alloc types.GenesisAlloc `json:"alloc,omitempty"`
|
||||
Env *stEnv `json:"env,omitempty"`
|
||||
Txs []*txWithKey `json:"txs,omitempty"`
|
||||
TxRlp string `json:"txsRlp,omitempty"`
|
||||
}
|
||||
|
||||
func Transition(ctx *cli.Context) error {
|
||||
@@ -272,7 +271,7 @@ func applyCancunChecks(env *stEnv, chainConfig *params.ChainConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type Alloc map[common.Address]core.GenesisAccount
|
||||
type Alloc map[common.Address]types.Account
|
||||
|
||||
func (g Alloc) OnRoot(common.Hash) {}
|
||||
|
||||
@@ -288,7 +287,7 @@ func (g Alloc) OnAccount(addr *common.Address, dumpAccount state.DumpAccount) {
|
||||
storage[k] = common.HexToHash(v)
|
||||
}
|
||||
}
|
||||
genesisAccount := core.GenesisAccount{
|
||||
genesisAccount := types.Account{
|
||||
Code: dumpAccount.Code,
|
||||
Storage: storage,
|
||||
Balance: balance,
|
||||
|
||||
@@ -38,8 +38,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -148,7 +148,7 @@ func runCmd(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
triedb := trie.NewDatabase(db, &trie.Config{
|
||||
triedb := triedb.NewDatabase(db, &triedb.Config{
|
||||
Preimages: preimages,
|
||||
HashDB: hashdb.Defaults,
|
||||
})
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/state/snapshot"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers/logger"
|
||||
"github.com/ethereum/go-ethereum/tests"
|
||||
@@ -90,26 +89,27 @@ func runStateTest(fname string, cfg vm.Config, jsonOut, dump bool) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var tests map[string]tests.StateTest
|
||||
if err := json.Unmarshal(src, &tests); err != nil {
|
||||
var testsByName map[string]tests.StateTest
|
||||
if err := json.Unmarshal(src, &testsByName); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Iterate over all the tests, run them and aggregate the results
|
||||
results := make([]StatetestResult, 0, len(tests))
|
||||
for key, test := range tests {
|
||||
results := make([]StatetestResult, 0, len(testsByName))
|
||||
for key, test := range testsByName {
|
||||
for _, st := range test.Subtests() {
|
||||
// Run the test and aggregate the result
|
||||
result := &StatetestResult{Name: key, Fork: st.Fork, Pass: true}
|
||||
test.Run(st, cfg, false, rawdb.HashScheme, func(err error, snaps *snapshot.Tree, statedb *state.StateDB) {
|
||||
test.Run(st, cfg, false, rawdb.HashScheme, func(err error, tstate *tests.StateTestState) {
|
||||
var root common.Hash
|
||||
if statedb != nil {
|
||||
root = statedb.IntermediateRoot(false)
|
||||
if tstate.StateDB != nil {
|
||||
root = tstate.StateDB.IntermediateRoot(false)
|
||||
result.Root = &root
|
||||
if jsonOut {
|
||||
fmt.Fprintf(os.Stderr, "{\"stateRoot\": \"%#x\"}\n", root)
|
||||
}
|
||||
if dump { // Dump any state to aid debugging
|
||||
cpy, _ := state.New(root, statedb.Database(), nil)
|
||||
cpy, _ := state.New(root, tstate.StateDB.Database(), nil)
|
||||
dump := cpy.RawDump(nil)
|
||||
result.State = &dump
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ type Env struct {
|
||||
CurrentTimestamp uint64 `json:"currentTimestamp"`
|
||||
Withdrawals []*Withdrawal `json:"withdrawals"`
|
||||
// optional
|
||||
CurrentDifficulty *big.Int `json:"currentDifficuly"`
|
||||
CurrentDifficulty *big.Int `json:"currentDifficulty"`
|
||||
CurrentRandom *big.Int `json:"currentRandom"`
|
||||
CurrentBaseFee *big.Int `json:"currentBaseFee"`
|
||||
ParentDifficulty *big.Int `json:"parentDifficulty"`
|
||||
|
||||
@@ -5,6 +5,7 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
@@ -78,7 +79,7 @@ func parseExtra(hexData string) (*Extra, error) {
|
||||
// decode hex into bytes
|
||||
data, err := hex.DecodeString(strings.TrimPrefix(hexData, "0x"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid hex data")
|
||||
return nil, errors.New("invalid hex data")
|
||||
}
|
||||
|
||||
// parse ExtraVanity and ExtraSeal
|
||||
@@ -99,7 +100,7 @@ func parseExtra(hexData string) (*Extra, error) {
|
||||
validatorNum := int(data[0])
|
||||
validatorBytesTotalLength := validatorNumberSize + validatorNum*validatorBytesLength
|
||||
if dataLength < validatorBytesTotalLength {
|
||||
return nil, fmt.Errorf("parse validators failed")
|
||||
return nil, errors.New("parse validators failed")
|
||||
}
|
||||
extra.ValidatorSize = uint8(validatorNum)
|
||||
data = data[validatorNumberSize:]
|
||||
@@ -117,7 +118,7 @@ func parseExtra(hexData string) (*Extra, error) {
|
||||
// parse Vote Attestation
|
||||
if dataLength > 0 {
|
||||
if err := rlp.Decode(bytes.NewReader(data), &extra.VoteAttestation); err != nil {
|
||||
return nil, fmt.Errorf("parse voteAttestation failed")
|
||||
return nil, errors.New("parse voteAttestation failed")
|
||||
}
|
||||
if extra.ValidatorSize > 0 {
|
||||
validatorsBitSet := bitset.From([]uint64{uint64(extra.VoteAddressSet)})
|
||||
|
||||
@@ -49,6 +49,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/gorilla/websocket"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -216,6 +217,8 @@ type faucet struct {
|
||||
|
||||
bep2eInfos map[string]bep2eInfo
|
||||
bep2eAbi abi.ABI
|
||||
|
||||
limiter *IPRateLimiter
|
||||
}
|
||||
|
||||
// wsConn wraps a websocket connection with a write mutex as the underlying
|
||||
@@ -235,6 +238,12 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Allow 1 request per minute with burst of 5, and cache up to 1000 IPs
|
||||
limiter, err := NewIPRateLimiter(rate.Limit(1.0), 5, 1000)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &faucet{
|
||||
config: genesis.Config,
|
||||
client: client,
|
||||
@@ -245,6 +254,7 @@ func newFaucet(genesis *core.Genesis, url string, ks *keystore.KeyStore, index [
|
||||
update: make(chan struct{}, 1),
|
||||
bep2eInfos: bep2eInfos,
|
||||
bep2eAbi: bep2eAbi,
|
||||
limiter: limiter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -272,6 +282,20 @@ func (f *faucet) webHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// apiHandler handles requests for Ether grants and transaction statuses.
|
||||
func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
ip := r.RemoteAddr
|
||||
if len(r.Header.Get("X-Forwarded-For")) > 0 {
|
||||
ips := strings.Split(r.Header.Get("X-Forwarded-For"), ",")
|
||||
if len(ips) > 0 {
|
||||
ip = strings.TrimSpace(ips[len(ips)-1])
|
||||
}
|
||||
}
|
||||
|
||||
if !f.limiter.GetLimiter(ip).Allow() {
|
||||
log.Warn("Too many requests from client: ", "client", ip)
|
||||
http.Error(w, "Too many requests", http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
|
||||
upgrader := websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
|
||||
conn, err := upgrader.Upgrade(w, r, nil)
|
||||
if err != nil {
|
||||
@@ -525,7 +549,7 @@ func (f *faucet) apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
f.lock.Unlock()
|
||||
|
||||
// Send an error if too frequent funding, othewise a success
|
||||
// Send an error if too frequent funding, otherwise a success
|
||||
if !fund {
|
||||
if err = sendError(wsconn, fmt.Errorf("%s left until next allowance", common.PrettyDuration(time.Until(timeout)))); err != nil { // nolint: gosimple
|
||||
log.Warn("Failed to send funding error to client", "err", err)
|
||||
@@ -625,24 +649,27 @@ func (f *faucet) loop() {
|
||||
balance := new(big.Int).Div(f.balance, ether)
|
||||
|
||||
for _, conn := range f.conns {
|
||||
if err := send(conn, map[string]interface{}{
|
||||
"funds": balance,
|
||||
"funded": f.nonce,
|
||||
"requests": f.reqs,
|
||||
}, time.Second); err != nil {
|
||||
log.Warn("Failed to send stats to client", "err", err)
|
||||
conn.conn.Close()
|
||||
continue
|
||||
}
|
||||
if err := send(conn, head, time.Second); err != nil {
|
||||
log.Warn("Failed to send header to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
go func(conn *wsConn) {
|
||||
if err := send(conn, map[string]interface{}{
|
||||
"funds": balance,
|
||||
"funded": f.nonce,
|
||||
"requests": f.reqs,
|
||||
}, time.Second); err != nil {
|
||||
log.Warn("Failed to send stats to client", "err", err)
|
||||
conn.conn.Close()
|
||||
return // Exit the goroutine if the first send fails
|
||||
}
|
||||
|
||||
if err := send(conn, head, time.Second); err != nil {
|
||||
log.Warn("Failed to send header to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
f.lock.RUnlock()
|
||||
}
|
||||
}()
|
||||
// Wait for various events and assing to the appropriate background threads
|
||||
// Wait for various events and assign to the appropriate background threads
|
||||
for {
|
||||
select {
|
||||
case head := <-heads:
|
||||
@@ -656,10 +683,12 @@ func (f *faucet) loop() {
|
||||
// Pending requests updated, stream to clients
|
||||
f.lock.RLock()
|
||||
for _, conn := range f.conns {
|
||||
if err := send(conn, map[string]interface{}{"requests": f.reqs}, time.Second); err != nil {
|
||||
log.Warn("Failed to send requests to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
go func(conn *wsConn) {
|
||||
if err := send(conn, map[string]interface{}{"requests": f.reqs}, time.Second); err != nil {
|
||||
log.Warn("Failed to send requests to client", "err", err)
|
||||
conn.conn.Close()
|
||||
}
|
||||
}(conn)
|
||||
}
|
||||
f.lock.RUnlock()
|
||||
}
|
||||
|
||||
44
cmd/faucet/rate_limiter.go
Normal file
44
cmd/faucet/rate_limiter.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"golang.org/x/time/rate"
|
||||
)
|
||||
|
||||
type IPRateLimiter struct {
|
||||
ips *lru.Cache // LRU cache to store IP addresses and their associated rate limiters
|
||||
r rate.Limit // the rate limit, e.g., 5 requests per second
|
||||
b int // the burst size, e.g., allowing a burst of 10 requests at once. The rate limiter gets into action
|
||||
// only after this number exceeds
|
||||
}
|
||||
|
||||
func NewIPRateLimiter(r rate.Limit, b int, size int) (*IPRateLimiter, error) {
|
||||
cache, err := lru.New(size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
i := &IPRateLimiter{
|
||||
ips: cache,
|
||||
r: r,
|
||||
b: b,
|
||||
}
|
||||
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func (i *IPRateLimiter) addIP(ip string) *rate.Limiter {
|
||||
limiter := rate.NewLimiter(i.r, i.b)
|
||||
|
||||
i.ips.Add(ip, limiter)
|
||||
|
||||
return limiter
|
||||
}
|
||||
|
||||
func (i *IPRateLimiter) GetLimiter(ip string) *rate.Limiter {
|
||||
if limiter, exists := i.ips.Get(ip); exists {
|
||||
return limiter.(*rate.Limiter)
|
||||
}
|
||||
|
||||
return i.addIP(ip)
|
||||
}
|
||||
@@ -48,7 +48,7 @@ func TestAttachWithHeaders(t *testing.T) {
|
||||
// This is fixed in a follow-up PR.
|
||||
}
|
||||
|
||||
// TestAttachWithHeaders tests that 'geth db --remotedb' with custom headers works, i.e
|
||||
// TestRemoteDbWithHeaders tests that 'geth db --remotedb' with custom headers works, i.e
|
||||
// that custom headers are forwarded to the target.
|
||||
func TestRemoteDbWithHeaders(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
@@ -12,16 +12,16 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v4/io/prompt"
|
||||
validatorpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts/iface"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts/petnames"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v4/validator/keymanager/local"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v5/encoding/bytesutil"
|
||||
"github.com/prysmaticlabs/prysm/v5/io/prompt"
|
||||
validatorpb "github.com/prysmaticlabs/prysm/v5/proto/prysm/v1alpha1/validator-client"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/iface"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/petnames"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/accounts/wallet"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/keymanager"
|
||||
"github.com/prysmaticlabs/prysm/v5/validator/keymanager/local"
|
||||
"github.com/urfave/cli/v2"
|
||||
keystorev4 "github.com/wealdtech/go-eth2-wallet-encryptor-keystorev4"
|
||||
|
||||
@@ -199,7 +199,7 @@ Delete the selected BLS account from the BLS wallet.`,
|
||||
Name: "generate-proof",
|
||||
Usage: "Generate ownership proof for the selected BLS account from the BLS wallet",
|
||||
Action: blsAccountGenerateProof,
|
||||
ArgsUsage: "<BLS pubkey>",
|
||||
ArgsUsage: "<operator address> <BLS pubkey>",
|
||||
Category: "BLS ACCOUNT COMMANDS",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
|
||||
@@ -41,14 +41,17 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/internal/era"
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -59,8 +62,9 @@ var (
|
||||
ArgsUsage: "<genesisPath>",
|
||||
Flags: flags.Merge([]cli.Flag{
|
||||
utils.CachePreimagesFlag,
|
||||
utils.OverrideCancun,
|
||||
utils.OverrideBohr,
|
||||
utils.OverrideVerkle,
|
||||
utils.MultiDataBaseFlag,
|
||||
}, utils.DatabaseFlags),
|
||||
Description: `
|
||||
The init command initializes a new genesis block and definition for the network.
|
||||
@@ -148,6 +152,33 @@ Optional second and third arguments control the first and
|
||||
last block to write. In this mode, the file will be appended
|
||||
if already existing. If the file ends with .gz, the output will
|
||||
be gzipped.`,
|
||||
}
|
||||
importHistoryCommand = &cli.Command{
|
||||
Action: importHistory,
|
||||
Name: "import-history",
|
||||
Usage: "Import an Era archive",
|
||||
ArgsUsage: "<dir>",
|
||||
Flags: flags.Merge([]cli.Flag{
|
||||
utils.TxLookupLimitFlag,
|
||||
},
|
||||
utils.DatabaseFlags,
|
||||
utils.NetworkFlags,
|
||||
),
|
||||
Description: `
|
||||
The import-history command will import blocks and their corresponding receipts
|
||||
from Era archives.
|
||||
`,
|
||||
}
|
||||
exportHistoryCommand = &cli.Command{
|
||||
Action: exportHistory,
|
||||
Name: "export-history",
|
||||
Usage: "Export blockchain history to Era archives",
|
||||
ArgsUsage: "<dir> <first> <last>",
|
||||
Flags: flags.Merge(utils.DatabaseFlags),
|
||||
Description: `
|
||||
The export-history command will export blocks and their corresponding receipts
|
||||
into Era archives. Eras are typically packaged in steps of 8192 blocks.
|
||||
`,
|
||||
}
|
||||
importPreimagesCommand = &cli.Command{
|
||||
Action: importPreimages,
|
||||
@@ -222,9 +253,9 @@ func initGenesis(ctx *cli.Context) error {
|
||||
defer stack.Close()
|
||||
|
||||
var overrides core.ChainOverrides
|
||||
if ctx.IsSet(utils.OverrideCancun.Name) {
|
||||
v := ctx.Uint64(utils.OverrideCancun.Name)
|
||||
overrides.OverrideCancun = &v
|
||||
if ctx.IsSet(utils.OverrideBohr.Name) {
|
||||
v := ctx.Uint64(utils.OverrideBohr.Name)
|
||||
overrides.OverrideBohr = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||
@@ -237,7 +268,22 @@ func initGenesis(ctx *cli.Context) error {
|
||||
}
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
// if the trie data dir has been set, new trie db with a new state database
|
||||
if ctx.IsSet(utils.MultiDataBaseFlag.Name) {
|
||||
statediskdb, dbErr := stack.OpenDatabaseWithFreezer(name+"/state", 0, 0, "", "", false, false, false, false)
|
||||
if dbErr != nil {
|
||||
utils.Fatalf("Failed to open separate trie database: %v", dbErr)
|
||||
}
|
||||
chaindb.SetStateStore(statediskdb)
|
||||
blockdb, err := stack.OpenDatabaseWithFreezer(name+"/block", 0, 0, "", "", false, false, false, false)
|
||||
if err != nil {
|
||||
utils.Fatalf("Failed to open separate block database: %v", err)
|
||||
}
|
||||
chaindb.SetBlockStore(blockdb)
|
||||
log.Warn("Multi-database is an experimental feature")
|
||||
}
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
|
||||
defer triedb.Close()
|
||||
|
||||
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||
@@ -435,6 +481,13 @@ func dumpGenesis(ctx *cli.Context) error {
|
||||
}
|
||||
continue
|
||||
}
|
||||
// set the separate state & block database
|
||||
if stack.CheckIfMultiDataBase() && err == nil {
|
||||
stateDiskDb := utils.MakeStateDataBase(ctx, stack, true, false)
|
||||
db.SetStateStore(stateDiskDb)
|
||||
blockDb := utils.MakeBlockDatabase(ctx, stack, true, false)
|
||||
db.SetBlockStore(blockDb)
|
||||
}
|
||||
genesis, err := core.ReadGenesis(db)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis: %s", err)
|
||||
@@ -462,10 +515,16 @@ func importChain(ctx *cli.Context) error {
|
||||
// Start system runtime metrics collection
|
||||
go metrics.CollectProcessMetrics(3 * time.Second)
|
||||
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
stack, cfg := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
chain, db := utils.MakeChain(ctx, stack, false)
|
||||
backend, err := eth.New(stack, &cfg.Eth)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
chain := backend.BlockChain()
|
||||
db := backend.ChainDb()
|
||||
defer db.Close()
|
||||
|
||||
// Start periodically gathering memory profiles
|
||||
@@ -563,7 +622,95 @@ func exportChain(ctx *cli.Context) error {
|
||||
}
|
||||
err = utils.ExportAppendChain(chain, fp, uint64(first), uint64(last))
|
||||
}
|
||||
if err != nil {
|
||||
utils.Fatalf("Export error: %v\n", err)
|
||||
}
|
||||
fmt.Printf("Export done in %v\n", time.Since(start))
|
||||
return nil
|
||||
}
|
||||
|
||||
func importHistory(ctx *cli.Context) error {
|
||||
if ctx.Args().Len() != 1 {
|
||||
utils.Fatalf("usage: %s", ctx.Command.ArgsUsage)
|
||||
}
|
||||
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
chain, db := utils.MakeChain(ctx, stack, false)
|
||||
defer db.Close()
|
||||
|
||||
var (
|
||||
start = time.Now()
|
||||
dir = ctx.Args().Get(0)
|
||||
network string
|
||||
)
|
||||
|
||||
// Determine network.
|
||||
if utils.IsNetworkPreset(ctx) {
|
||||
switch {
|
||||
case ctx.Bool(utils.BSCMainnetFlag.Name):
|
||||
network = "mainnet"
|
||||
case ctx.Bool(utils.ChapelFlag.Name):
|
||||
network = "chapel"
|
||||
}
|
||||
} else {
|
||||
// No network flag set, try to determine network based on files
|
||||
// present in directory.
|
||||
var networks []string
|
||||
for _, n := range params.NetworkNames {
|
||||
entries, err := era.ReadDir(dir, n)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading %s: %w", dir, err)
|
||||
}
|
||||
if len(entries) > 0 {
|
||||
networks = append(networks, n)
|
||||
}
|
||||
}
|
||||
if len(networks) == 0 {
|
||||
return fmt.Errorf("no era1 files found in %s", dir)
|
||||
}
|
||||
if len(networks) > 1 {
|
||||
return fmt.Errorf("multiple networks found, use a network flag to specify desired network")
|
||||
}
|
||||
network = networks[0]
|
||||
}
|
||||
|
||||
if err := utils.ImportHistory(chain, db, dir, network); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("Import done in %v\n", time.Since(start))
|
||||
return nil
|
||||
}
|
||||
|
||||
// exportHistory exports chain history in Era archives at a specified
|
||||
// directory.
|
||||
func exportHistory(ctx *cli.Context) error {
|
||||
if ctx.Args().Len() != 3 {
|
||||
utils.Fatalf("usage: %s", ctx.Command.ArgsUsage)
|
||||
}
|
||||
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
chain, _ := utils.MakeChain(ctx, stack, true)
|
||||
start := time.Now()
|
||||
|
||||
var (
|
||||
dir = ctx.Args().Get(0)
|
||||
first, ferr = strconv.ParseInt(ctx.Args().Get(1), 10, 64)
|
||||
last, lerr = strconv.ParseInt(ctx.Args().Get(2), 10, 64)
|
||||
)
|
||||
if ferr != nil || lerr != nil {
|
||||
utils.Fatalf("Export error in parsing parameters: block number not an integer\n")
|
||||
}
|
||||
if first < 0 || last < 0 {
|
||||
utils.Fatalf("Export error: block number must be greater than 0\n")
|
||||
}
|
||||
if head := chain.CurrentSnapBlock(); uint64(last) > head.Number.Uint64() {
|
||||
utils.Fatalf("Export error: block number %d larger than head block %d\n", uint64(last), head.Number.Uint64())
|
||||
}
|
||||
err := utils.ExportHistory(chain, dir, uint64(first), uint64(last), uint64(era.MaxEra1Size))
|
||||
if err != nil {
|
||||
utils.Fatalf("Export error: %v\n", err)
|
||||
}
|
||||
@@ -600,7 +747,6 @@ func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, eth
|
||||
}
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
scheme, err := rawdb.ParseStateScheme(ctx.String(utils.StateSchemeFlag.Name), db)
|
||||
if err != nil {
|
||||
return nil, nil, common.Hash{}, err
|
||||
@@ -609,7 +755,7 @@ func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, eth
|
||||
fmt.Println("You are using geth dump in path mode, please use `geth dump-roothash` command to get all available blocks.")
|
||||
}
|
||||
|
||||
var header *types.Header
|
||||
header := &types.Header{}
|
||||
if ctx.NArg() == 1 {
|
||||
arg := ctx.Args().First()
|
||||
if hashish(arg) {
|
||||
@@ -633,12 +779,12 @@ func parseDumpConfig(ctx *cli.Context, stack *node.Node) (*state.DumpConfig, eth
|
||||
} else {
|
||||
// Use latest
|
||||
if scheme == rawdb.PathScheme {
|
||||
triedb := trie.NewDatabase(db, &trie.Config{PathDB: pathdb.ReadOnly})
|
||||
triedb := triedb.NewDatabase(db, &triedb.Config{PathDB: utils.PathDBConfigAddJournalFilePath(stack, pathdb.ReadOnly)})
|
||||
defer triedb.Close()
|
||||
if stateRoot := triedb.Head(); stateRoot != (common.Hash{}) {
|
||||
header.Root = stateRoot
|
||||
} else {
|
||||
return nil, nil, common.Hash{}, fmt.Errorf("no top state root hash in path db")
|
||||
return nil, nil, common.Hash{}, errors.New("no top state root hash in path db")
|
||||
}
|
||||
} else {
|
||||
header = rawdb.ReadHeadHeader(db)
|
||||
@@ -683,7 +829,8 @@ func dump(ctx *cli.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup
|
||||
defer db.Close()
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, true, true, false) // always enable preimage lookup
|
||||
defer triedb.Close()
|
||||
|
||||
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||
@@ -703,7 +850,7 @@ func dumpAllRootHashInPath(ctx *cli.Context) error {
|
||||
defer stack.Close()
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
triedb := trie.NewDatabase(db, &trie.Config{PathDB: pathdb.ReadOnly})
|
||||
triedb := triedb.NewDatabase(db, &triedb.Config{PathDB: utils.PathDBConfigAddJournalFilePath(stack, pathdb.ReadOnly)})
|
||||
defer triedb.Close()
|
||||
|
||||
scheme, err := rawdb.ParseStateScheme(ctx.String(utils.StateSchemeFlag.Name), db)
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/ethereum/go-ethereum/eth/downloader"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/external"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
@@ -183,26 +185,28 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||
params.RialtoGenesisHash = common.HexToHash(v)
|
||||
}
|
||||
|
||||
if ctx.IsSet(utils.OverrideShanghai.Name) {
|
||||
v := ctx.Uint64(utils.OverrideShanghai.Name)
|
||||
cfg.Eth.OverrideShanghai = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideKepler.Name) {
|
||||
v := ctx.Uint64(utils.OverrideKepler.Name)
|
||||
cfg.Eth.OverrideKepler = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideCancun.Name) {
|
||||
v := ctx.Uint64(utils.OverrideCancun.Name)
|
||||
cfg.Eth.OverrideCancun = &v
|
||||
if ctx.IsSet(utils.OverrideBohr.Name) {
|
||||
v := ctx.Uint64(utils.OverrideBohr.Name)
|
||||
cfg.Eth.OverrideBohr = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideVerkle.Name) {
|
||||
v := ctx.Uint64(utils.OverrideVerkle.Name)
|
||||
cfg.Eth.OverrideVerkle = &v
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideFeynman.Name) {
|
||||
v := ctx.Uint64(utils.OverrideFeynman.Name)
|
||||
cfg.Eth.OverrideFeynman = &v
|
||||
if ctx.IsSet(utils.OverrideFullImmutabilityThreshold.Name) {
|
||||
params.FullImmutabilityThreshold = ctx.Uint64(utils.OverrideFullImmutabilityThreshold.Name)
|
||||
downloader.FullMaxForkAncestry = ctx.Uint64(utils.OverrideFullImmutabilityThreshold.Name)
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideMinBlocksForBlobRequests.Name) {
|
||||
params.MinBlocksForBlobRequests = ctx.Uint64(utils.OverrideMinBlocksForBlobRequests.Name)
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideDefaultExtraReserveForBlobRequests.Name) {
|
||||
params.DefaultExtraReserveForBlobRequests = ctx.Uint64(utils.OverrideDefaultExtraReserveForBlobRequests.Name)
|
||||
}
|
||||
if ctx.IsSet(utils.OverrideBreatheBlockInterval.Name) {
|
||||
params.BreatheBlockInterval = ctx.Uint64(utils.OverrideBreatheBlockInterval.Name)
|
||||
}
|
||||
|
||||
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
|
||||
|
||||
// Create gauge with geth system and build information
|
||||
@@ -234,8 +238,8 @@ func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
|
||||
git, _ := version.VCS()
|
||||
utils.SetupMetrics(ctx,
|
||||
utils.EnableBuildInfo(git.Commit, git.Date),
|
||||
utils.EnableMinerInfo(ctx, cfg.Eth.Miner),
|
||||
utils.EnableNodeInfo(cfg.Eth.TxPool),
|
||||
utils.EnableMinerInfo(ctx, &cfg.Eth.Miner),
|
||||
utils.EnableNodeInfo(&cfg.Eth.TxPool, stack.Server().NodeInfo()),
|
||||
)
|
||||
return stack, backend
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 parlia:1.0 rpc:1.0 txpool:1.0 web3:1.0"
|
||||
ipcAPIs = "admin:1.0 debug:1.0 eth:1.0 mev:1.0 miner:1.0 net:1.0 parlia:1.0 rpc:1.0 txpool:1.0 web3:1.0"
|
||||
httpAPIs = "eth:1.0 net:1.0 rpc:1.0 web3:1.0"
|
||||
)
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
@@ -39,7 +40,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/internal/flags"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
@@ -74,6 +76,7 @@ Remove blockchain and state databases`,
|
||||
dbCompactCmd,
|
||||
dbGetCmd,
|
||||
dbDeleteCmd,
|
||||
dbDeleteTrieStateCmd,
|
||||
dbInspectTrieCmd,
|
||||
dbPutCmd,
|
||||
dbGetSlotsCmd,
|
||||
@@ -103,12 +106,12 @@ Remove blockchain and state databases`,
|
||||
dbInspectTrieCmd = &cli.Command{
|
||||
Action: inspectTrie,
|
||||
Name: "inspect-trie",
|
||||
ArgsUsage: "<blocknum> <jobnum>",
|
||||
ArgsUsage: "<blocknum> <jobnum> <topn>",
|
||||
Flags: []cli.Flag{
|
||||
utils.DataDirFlag,
|
||||
utils.SyncModeFlag,
|
||||
},
|
||||
Usage: "Inspect the MPT tree of the account and contract.",
|
||||
Usage: "Inspect the MPT tree of the account and contract. 'blocknum' can be latest/snapshot/number. 'topn' means output the top N storage tries info ranked by the total number of TrieNodes",
|
||||
Description: `This commands iterates the entrie WorldState.`,
|
||||
}
|
||||
dbCheckStateContentCmd = &cli.Command{
|
||||
@@ -204,6 +207,15 @@ corruption if it is aborted during execution'!`,
|
||||
Description: `This command deletes the specified database key from the database.
|
||||
WARNING: This is a low-level operation which may cause database corruption!`,
|
||||
}
|
||||
dbDeleteTrieStateCmd = &cli.Command{
|
||||
Action: dbDeleteTrieState,
|
||||
Name: "delete-trie-state",
|
||||
Usage: "Delete all trie state key-value pairs from the database and the ancient state. Does not support hash-based state scheme.",
|
||||
Flags: flags.Merge([]cli.Flag{
|
||||
utils.SyncModeFlag,
|
||||
}, utils.NetworkFlags, utils.DatabaseFlags),
|
||||
Description: `This command deletes all trie state key-value pairs from the database and the ancient state.`,
|
||||
}
|
||||
dbPutCmd = &cli.Command{
|
||||
Action: dbPut,
|
||||
Name: "put",
|
||||
@@ -374,6 +386,7 @@ func inspectTrie(ctx *cli.Context) error {
|
||||
blockNumber uint64
|
||||
trieRootHash common.Hash
|
||||
jobnum uint64
|
||||
topN uint64
|
||||
)
|
||||
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
@@ -381,7 +394,6 @@ func inspectTrie(ctx *cli.Context) error {
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
|
||||
var headerBlockHash common.Hash
|
||||
if ctx.NArg() >= 1 {
|
||||
if ctx.Args().Get(0) == "latest" {
|
||||
@@ -394,24 +406,37 @@ func inspectTrie(ctx *cli.Context) error {
|
||||
var err error
|
||||
blockNumber, err = strconv.ParseUint(ctx.Args().Get(0), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to Parse blocknum, Args[0]: %v, err: %v", ctx.Args().Get(0), err)
|
||||
return fmt.Errorf("failed to parse blocknum, Args[0]: %v, err: %v", ctx.Args().Get(0), err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.NArg() == 1 {
|
||||
jobnum = 1000
|
||||
topN = 10
|
||||
} else if ctx.NArg() == 2 {
|
||||
var err error
|
||||
jobnum, err = strconv.ParseUint(ctx.Args().Get(1), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse jobnum, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
|
||||
}
|
||||
topN = 10
|
||||
} else {
|
||||
var err error
|
||||
jobnum, err = strconv.ParseUint(ctx.Args().Get(1), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to Parse jobnum, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
|
||||
return fmt.Errorf("failed to parse jobnum, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
|
||||
}
|
||||
|
||||
topN, err = strconv.ParseUint(ctx.Args().Get(2), 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse topn, Args[1]: %v, err: %v", ctx.Args().Get(1), err)
|
||||
}
|
||||
}
|
||||
|
||||
if blockNumber != math.MaxUint64 {
|
||||
headerBlockHash = rawdb.ReadCanonicalHash(db, blockNumber)
|
||||
if headerBlockHash == (common.Hash{}) {
|
||||
return fmt.Errorf("ReadHeadBlockHash empry hash")
|
||||
return errors.New("ReadHeadBlockHash empty hash")
|
||||
}
|
||||
blockHeader := rawdb.ReadHeader(db, headerBlockHash, blockNumber)
|
||||
trieRootHash = blockHeader.Root
|
||||
@@ -422,22 +447,23 @@ func inspectTrie(ctx *cli.Context) error {
|
||||
fmt.Printf("ReadBlockHeader, root: %v, blocknum: %v\n", trieRootHash, blockNumber)
|
||||
|
||||
dbScheme := rawdb.ReadStateScheme(db)
|
||||
var config *trie.Config
|
||||
var config *triedb.Config
|
||||
if dbScheme == rawdb.PathScheme {
|
||||
config = &trie.Config{
|
||||
PathDB: pathdb.ReadOnly,
|
||||
config = &triedb.Config{
|
||||
PathDB: utils.PathDBConfigAddJournalFilePath(stack, pathdb.ReadOnly),
|
||||
Cache: 0,
|
||||
}
|
||||
} else if dbScheme == rawdb.HashScheme {
|
||||
config = trie.HashDefaults
|
||||
config = triedb.HashDefaults
|
||||
}
|
||||
|
||||
triedb := trie.NewDatabase(db, config)
|
||||
triedb := triedb.NewDatabase(db, config)
|
||||
theTrie, err := trie.New(trie.TrieID(trieRootHash), triedb)
|
||||
if err != nil {
|
||||
fmt.Printf("fail to new trie tree, err: %v, rootHash: %v\n", err, trieRootHash.String())
|
||||
return err
|
||||
}
|
||||
theInspect, err := trie.NewInspector(theTrie, triedb, trieRootHash, blockNumber, jobnum)
|
||||
theInspect, err := trie.NewInspector(theTrie, triedb, trieRootHash, blockNumber, jobnum, int(topN))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -482,7 +508,7 @@ func ancientInspect(ctx *cli.Context) error {
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, true)
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
return rawdb.AncientInspect(db)
|
||||
}
|
||||
@@ -508,7 +534,7 @@ func checkStateContent(ctx *cli.Context) error {
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
var (
|
||||
it = rawdb.NewKeyLengthIterator(db.NewIterator(prefix, start), 32)
|
||||
it ethdb.Iterator
|
||||
hasher = crypto.NewKeccakState()
|
||||
got = make([]byte, 32)
|
||||
errs int
|
||||
@@ -516,6 +542,11 @@ func checkStateContent(ctx *cli.Context) error {
|
||||
startTime = time.Now()
|
||||
lastLog = time.Now()
|
||||
)
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
it = rawdb.NewKeyLengthIterator(db.StateStore().NewIterator(prefix, start), 32)
|
||||
} else {
|
||||
it = rawdb.NewKeyLengthIterator(db.NewIterator(prefix, start), 32)
|
||||
}
|
||||
for it.Next() {
|
||||
count++
|
||||
k := it.Key()
|
||||
@@ -562,6 +593,13 @@ func dbStats(ctx *cli.Context) error {
|
||||
defer db.Close()
|
||||
|
||||
showLeveldbStats(db)
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
fmt.Println("show stats of state store")
|
||||
showLeveldbStats(db.StateStore())
|
||||
fmt.Println("show stats of block store")
|
||||
showLeveldbStats(db.BlockStore())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -575,13 +613,38 @@ func dbCompact(ctx *cli.Context) error {
|
||||
log.Info("Stats before compaction")
|
||||
showLeveldbStats(db)
|
||||
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
fmt.Println("show stats of state store")
|
||||
showLeveldbStats(db.StateStore())
|
||||
fmt.Println("show stats of block store")
|
||||
showLeveldbStats(db.BlockStore())
|
||||
}
|
||||
|
||||
log.Info("Triggering compaction")
|
||||
if err := db.Compact(nil, nil); err != nil {
|
||||
log.Info("Compact err", "error", err)
|
||||
log.Error("Compact err", "error", err)
|
||||
return err
|
||||
}
|
||||
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
if err := db.StateStore().Compact(nil, nil); err != nil {
|
||||
log.Error("Compact err", "error", err)
|
||||
return err
|
||||
}
|
||||
if err := db.BlockStore().Compact(nil, nil); err != nil {
|
||||
log.Error("Compact err", "error", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
log.Info("Stats after compaction")
|
||||
showLeveldbStats(db)
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
fmt.Println("show stats of state store after compaction")
|
||||
showLeveldbStats(db.StateStore())
|
||||
fmt.Println("show stats of block store after compaction")
|
||||
showLeveldbStats(db.BlockStore())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -601,8 +664,17 @@ func dbGet(ctx *cli.Context) error {
|
||||
log.Info("Could not decode the key", "error", err)
|
||||
return err
|
||||
}
|
||||
opDb := db
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
keyType := rawdb.DataTypeByKey(key)
|
||||
if keyType == rawdb.StateDataType {
|
||||
opDb = db.StateStore()
|
||||
} else if keyType == rawdb.BlockDataType {
|
||||
opDb = db.BlockStore()
|
||||
}
|
||||
}
|
||||
|
||||
data, err := db.Get(key)
|
||||
data, err := opDb.Get(key)
|
||||
if err != nil {
|
||||
log.Info("Get operation failed", "key", fmt.Sprintf("%#x", key), "error", err)
|
||||
return err
|
||||
@@ -619,8 +691,14 @@ func dbTrieGet(ctx *cli.Context) error {
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, false, false)
|
||||
defer db.Close()
|
||||
var db ethdb.Database
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
if chaindb.StateStore() != nil {
|
||||
db = chaindb.StateStore()
|
||||
} else {
|
||||
db = chaindb
|
||||
}
|
||||
defer chaindb.Close()
|
||||
|
||||
scheme := ctx.String(utils.StateSchemeFlag.Name)
|
||||
if scheme == "" {
|
||||
@@ -685,8 +763,14 @@ func dbTrieDelete(ctx *cli.Context) error {
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, false, false)
|
||||
defer db.Close()
|
||||
var db ethdb.Database
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
if chaindb.StateStore() != nil {
|
||||
db = chaindb.StateStore()
|
||||
} else {
|
||||
db = chaindb
|
||||
}
|
||||
defer chaindb.Close()
|
||||
|
||||
scheme := ctx.String(utils.StateSchemeFlag.Name)
|
||||
if scheme == "" {
|
||||
@@ -754,17 +838,103 @@ func dbDelete(ctx *cli.Context) error {
|
||||
log.Info("Could not decode the key", "error", err)
|
||||
return err
|
||||
}
|
||||
data, err := db.Get(key)
|
||||
opDb := db
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
keyType := rawdb.DataTypeByKey(key)
|
||||
if keyType == rawdb.StateDataType {
|
||||
opDb = db.StateStore()
|
||||
} else if keyType == rawdb.BlockDataType {
|
||||
opDb = db.BlockStore()
|
||||
}
|
||||
}
|
||||
|
||||
data, err := opDb.Get(key)
|
||||
if err == nil {
|
||||
fmt.Printf("Previous value: %#x\n", data)
|
||||
}
|
||||
if err = db.Delete(key); err != nil {
|
||||
if err = opDb.Delete(key); err != nil {
|
||||
log.Info("Delete operation returned an error", "key", fmt.Sprintf("%#x", key), "error", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// dbDeleteTrieState deletes all trie state related key-value pairs from the database and the ancient state store.
|
||||
func dbDeleteTrieState(ctx *cli.Context) error {
|
||||
if ctx.NArg() > 0 {
|
||||
return fmt.Errorf("no arguments required")
|
||||
}
|
||||
|
||||
stack, config := makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, false, false)
|
||||
defer db.Close()
|
||||
|
||||
var (
|
||||
err error
|
||||
start = time.Now()
|
||||
)
|
||||
|
||||
// If separate trie db exists, delete all files in the db folder
|
||||
if db.StateStore() != nil {
|
||||
statePath := filepath.Join(stack.ResolvePath("chaindata"), "state")
|
||||
log.Info("Removing separate trie database", "path", statePath)
|
||||
err = filepath.Walk(statePath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if path != statePath {
|
||||
fileInfo, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !fileInfo.IsDir() {
|
||||
os.Remove(path)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
log.Info("Separate trie database deleted", "err", err, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
return err
|
||||
}
|
||||
|
||||
// Delete KV pairs from the database
|
||||
err = rawdb.DeleteTrieState(db)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove the full node ancient database
|
||||
dbPath := config.Eth.DatabaseFreezer
|
||||
switch {
|
||||
case dbPath == "":
|
||||
dbPath = filepath.Join(stack.ResolvePath("chaindata"), "ancient/state")
|
||||
case !filepath.IsAbs(dbPath):
|
||||
dbPath = config.Node.ResolvePath(dbPath)
|
||||
}
|
||||
|
||||
if !common.FileExist(dbPath) {
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Info("Removing ancient state database", "path", dbPath)
|
||||
start = time.Now()
|
||||
filepath.Walk(dbPath, func(path string, info os.FileInfo, err error) error {
|
||||
if dbPath == path {
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() {
|
||||
os.Remove(path)
|
||||
return nil
|
||||
}
|
||||
return filepath.SkipDir
|
||||
})
|
||||
log.Info("State database successfully deleted", "path", dbPath, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// dbPut overwrite a value in the database
|
||||
func dbPut(ctx *cli.Context) error {
|
||||
if ctx.NArg() != 2 {
|
||||
@@ -792,11 +962,22 @@ func dbPut(ctx *cli.Context) error {
|
||||
log.Info("Could not decode the value", "error", err)
|
||||
return err
|
||||
}
|
||||
data, err = db.Get(key)
|
||||
|
||||
opDb := db
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
keyType := rawdb.DataTypeByKey(key)
|
||||
if keyType == rawdb.StateDataType {
|
||||
opDb = db.StateStore()
|
||||
} else if keyType == rawdb.BlockDataType {
|
||||
opDb = db.BlockStore()
|
||||
}
|
||||
}
|
||||
|
||||
data, err = opDb.Get(key)
|
||||
if err == nil {
|
||||
fmt.Printf("Previous value: %#x\n", data)
|
||||
}
|
||||
return db.Put(key, value)
|
||||
return opDb.Put(key, value)
|
||||
}
|
||||
|
||||
// dbDumpTrie shows the key-value slots of a given storage trie
|
||||
@@ -809,8 +990,7 @@ func dbDumpTrie(ctx *cli.Context) error {
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
var (
|
||||
@@ -888,7 +1068,7 @@ func freezerInspect(ctx *cli.Context) error {
|
||||
stack, _ := makeConfigNode(ctx)
|
||||
ancient := stack.ResolveAncient("chaindata", ctx.String(utils.AncientFlag.Name))
|
||||
stack.Close()
|
||||
return rawdb.InspectFreezerTable(ancient, freezer, table, start, end)
|
||||
return rawdb.InspectFreezerTable(ancient, freezer, table, start, end, stack.CheckIfMultiDataBase())
|
||||
}
|
||||
|
||||
func importLDBdata(ctx *cli.Context) error {
|
||||
@@ -1028,7 +1208,7 @@ func showMetaData(ctx *cli.Context) error {
|
||||
defer stack.Close()
|
||||
db := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer db.Close()
|
||||
ancients, err := db.Ancients()
|
||||
ancients, err := db.BlockStore().Ancients()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error accessing ancients: %v", err)
|
||||
}
|
||||
@@ -1075,14 +1255,20 @@ func hbss2pbss(ctx *cli.Context) error {
|
||||
defer stack.Close()
|
||||
|
||||
db := utils.MakeChainDatabase(ctx, stack, false, false)
|
||||
db.Sync()
|
||||
db.BlockStore().Sync()
|
||||
stateDiskDb := db.StateStore()
|
||||
defer db.Close()
|
||||
|
||||
// convert hbss trie node to pbss trie node
|
||||
lastStateID := rawdb.ReadPersistentStateID(db)
|
||||
var lastStateID uint64
|
||||
if stateDiskDb != nil {
|
||||
lastStateID = rawdb.ReadPersistentStateID(stateDiskDb)
|
||||
} else {
|
||||
lastStateID = rawdb.ReadPersistentStateID(db)
|
||||
}
|
||||
if lastStateID == 0 || force {
|
||||
config := trie.HashDefaults
|
||||
triedb := trie.NewDatabase(db, config)
|
||||
config := triedb.HashDefaults
|
||||
triedb := triedb.NewDatabase(db, config)
|
||||
triedb.Cap(0)
|
||||
log.Info("hbss2pbss triedb", "scheme", triedb.Scheme())
|
||||
defer triedb.Close()
|
||||
@@ -1102,7 +1288,7 @@ func hbss2pbss(ctx *cli.Context) error {
|
||||
if *blockNumber != math.MaxUint64 {
|
||||
headerBlockHash = rawdb.ReadCanonicalHash(db, *blockNumber)
|
||||
if headerBlockHash == (common.Hash{}) {
|
||||
return fmt.Errorf("ReadHeadBlockHash empty hash")
|
||||
return errors.New("ReadHeadBlockHash empty hash")
|
||||
}
|
||||
blockHeader := rawdb.ReadHeader(db, headerBlockHash, *blockNumber)
|
||||
trieRootHash = blockHeader.Root
|
||||
@@ -1110,7 +1296,7 @@ func hbss2pbss(ctx *cli.Context) error {
|
||||
}
|
||||
if (trieRootHash == common.Hash{}) {
|
||||
log.Error("Empty root hash")
|
||||
return fmt.Errorf("Empty root hash.")
|
||||
return errors.New("Empty root hash.")
|
||||
}
|
||||
|
||||
id := trie.StateTrieID(trieRootHash)
|
||||
@@ -1131,18 +1317,34 @@ func hbss2pbss(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
// repair state ancient offset
|
||||
lastStateID = rawdb.ReadPersistentStateID(db)
|
||||
if stateDiskDb != nil {
|
||||
lastStateID = rawdb.ReadPersistentStateID(stateDiskDb)
|
||||
} else {
|
||||
lastStateID = rawdb.ReadPersistentStateID(db)
|
||||
}
|
||||
|
||||
if lastStateID == 0 {
|
||||
log.Error("Convert hbss to pbss trie node error. The last state id is still 0")
|
||||
}
|
||||
ancient := stack.ResolveAncient("chaindata", ctx.String(utils.AncientFlag.Name))
|
||||
|
||||
var ancient string
|
||||
if db.StateStore() != nil {
|
||||
dirName := filepath.Join(stack.ResolvePath("chaindata"), "state")
|
||||
ancient = filepath.Join(dirName, "ancient")
|
||||
} else {
|
||||
ancient = stack.ResolveAncient("chaindata", ctx.String(utils.AncientFlag.Name))
|
||||
}
|
||||
err = rawdb.ResetStateFreezerTableOffset(ancient, lastStateID)
|
||||
if err != nil {
|
||||
log.Error("Reset state freezer table offset failed", "error", err)
|
||||
return err
|
||||
}
|
||||
// prune hbss trie node
|
||||
err = rawdb.PruneHashTrieNodeInDataBase(db)
|
||||
if stateDiskDb != nil {
|
||||
err = rawdb.PruneHashTrieNodeInDataBase(stateDiskDb)
|
||||
} else {
|
||||
err = rawdb.PruneHashTrieNodeInDataBase(db)
|
||||
}
|
||||
if err != nil {
|
||||
log.Error("Prune Hash trie node in database failed", "error", err)
|
||||
return err
|
||||
|
||||
@@ -151,8 +151,8 @@ func TestCustomBackend(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
for i, tt := range []backendTest{
|
||||
{ // When not specified, it should default to leveldb
|
||||
execArgs: []string{"--db.engine", "leveldb"},
|
||||
{ // When not specified, it should default to pebble
|
||||
execArgs: []string{"--db.engine", "pebble"},
|
||||
execExpect: "0x0000000000001338",
|
||||
},
|
||||
{ // Explicit leveldb
|
||||
|
||||
BIN
cmd/geth/geth
Executable file
BIN
cmd/geth/geth
Executable file
Binary file not shown.
@@ -25,6 +25,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
@@ -70,11 +72,12 @@ var (
|
||||
utils.USBFlag,
|
||||
utils.SmartCardDaemonPathFlag,
|
||||
utils.RialtoHash,
|
||||
utils.OverrideShanghai,
|
||||
utils.OverrideKepler,
|
||||
utils.OverrideCancun,
|
||||
utils.OverrideBohr,
|
||||
utils.OverrideVerkle,
|
||||
utils.OverrideFeynman,
|
||||
utils.OverrideFullImmutabilityThreshold,
|
||||
utils.OverrideMinBlocksForBlobRequests,
|
||||
utils.OverrideDefaultExtraReserveForBlobRequests,
|
||||
utils.OverrideBreatheBlockInterval,
|
||||
utils.EnablePersonal,
|
||||
utils.TxPoolLocalsFlag,
|
||||
utils.TxPoolNoLocalsFlag,
|
||||
@@ -101,6 +104,7 @@ var (
|
||||
utils.TransactionHistoryFlag,
|
||||
utils.StateHistoryFlag,
|
||||
utils.PathDBSyncFlag,
|
||||
utils.JournalFileFlag,
|
||||
utils.LightServeFlag, // deprecated
|
||||
utils.LightIngressFlag, // deprecated
|
||||
utils.LightEgressFlag, // deprecated
|
||||
@@ -121,6 +125,7 @@ var (
|
||||
utils.CacheSnapshotFlag,
|
||||
// utils.CacheNoPrefetchFlag,
|
||||
utils.CachePreimagesFlag,
|
||||
utils.MultiDataBaseFlag,
|
||||
utils.PersistDiffFlag,
|
||||
utils.DiffBlockFlag,
|
||||
utils.PruneAncientDataFlag,
|
||||
@@ -142,8 +147,10 @@ var (
|
||||
// utils.MinerNewPayloadTimeout,
|
||||
utils.NATFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.PeerFilterPatternsFlag,
|
||||
utils.DiscoveryV4Flag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.InstanceFlag,
|
||||
utils.LegacyDiscoveryV5Flag, // deprecated
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
@@ -172,6 +179,7 @@ var (
|
||||
utils.VoteJournalDirFlag,
|
||||
utils.LogDebugFlag,
|
||||
utils.LogBacktraceAtFlag,
|
||||
utils.BlobExtraReserveFlag,
|
||||
}, utils.NetworkFlags, utils.DatabaseFlags)
|
||||
|
||||
rpcFlags = []cli.Flag{
|
||||
@@ -235,6 +243,8 @@ func init() {
|
||||
initNetworkCommand,
|
||||
importCommand,
|
||||
exportCommand,
|
||||
importHistoryCommand,
|
||||
exportHistoryCommand,
|
||||
importPreimagesCommand,
|
||||
removedbCommand,
|
||||
dumpCommand,
|
||||
@@ -325,9 +335,6 @@ func prepare(ctx *cli.Context) {
|
||||
5. Networking is disabled; there is no listen-address, the maximum number of peers is set
|
||||
to 0, and discovery is disabled.
|
||||
`)
|
||||
|
||||
case !ctx.IsSet(utils.NetworkIdFlag.Name):
|
||||
log.Info("Starting Geth on BSC mainnet...")
|
||||
}
|
||||
// If we're a full node on mainnet without --cache specified, bump default cache allowance
|
||||
if !ctx.IsSet(utils.CacheFlag.Name) && !ctx.IsSet(utils.NetworkIdFlag.Name) {
|
||||
@@ -448,6 +455,10 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend, isCon
|
||||
// Set the gas price to the limits from the CLI and start mining
|
||||
gasprice := flags.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
|
||||
ethBackend.TxPool().SetGasTip(gasprice)
|
||||
gasCeil := ethBackend.Miner().GasCeil()
|
||||
if gasCeil > params.SystemTxsGas {
|
||||
ethBackend.TxPool().SetMaxGas(gasCeil - params.SystemTxsGas)
|
||||
}
|
||||
if err := ethBackend.StartMining(); err != nil {
|
||||
utils.Fatalf("Failed to start mining: %v", err)
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -51,7 +51,7 @@ var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
balance = big.NewInt(100000000000000000)
|
||||
gspec = &core.Genesis{Config: params.TestChainConfig, Alloc: core.GenesisAlloc{address: {Balance: balance}}, BaseFee: big.NewInt(params.InitialBaseFee)}
|
||||
gspec = &core.Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{address: {Balance: balance}}, BaseFee: big.NewInt(params.InitialBaseFee)}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
config = &core.CacheConfig{
|
||||
TrieCleanLimit: 256,
|
||||
@@ -75,7 +75,7 @@ func NewLevelDBDatabaseWithFreezer(file string, cache int, handles int, ancient
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
frdb, err := rawdb.NewDatabaseWithFreezer(kvdb, ancient, namespace, readonly, disableFreeze, isLastOffset, pruneAncientData)
|
||||
frdb, err := rawdb.NewDatabaseWithFreezer(kvdb, ancient, namespace, readonly, disableFreeze, isLastOffset, pruneAncientData, false)
|
||||
if err != nil {
|
||||
kvdb.Close()
|
||||
return nil, err
|
||||
@@ -152,9 +152,15 @@ func BlockchainCreator(t *testing.T, chaindbPath, AncientPath string, blockRemai
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
triedb := trie.NewDatabase(db, nil)
|
||||
triedb := triedb.NewDatabase(db, nil)
|
||||
defer triedb.Close()
|
||||
|
||||
if err = db.SetupFreezerEnv(ðdb.FreezerEnv{
|
||||
ChainCfg: gspec.Config,
|
||||
BlobExtraReserve: params.DefaultExtraReserveForBlobRequests,
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
genesis := gspec.MustCommit(db, triedb)
|
||||
// Initialize a fresh chain with only a genesis block
|
||||
blockchain, err := core.NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
|
||||
@@ -43,8 +43,11 @@ import (
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
cli "github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@@ -91,7 +94,7 @@ WARNING: it's only supported in hash mode(--state.scheme=hash)".
|
||||
geth offline prune-block for block data in ancientdb.
|
||||
The amount of blocks expected for remaining after prune can be specified via block-amount-reserved in this command,
|
||||
will prune and only remain the specified amount of old block data in ancientdb.
|
||||
the brief workflow is to backup the the number of this specified amount blocks backward in original ancientdb
|
||||
the brief workflow is to backup the number of this specified amount blocks backward in original ancientdb
|
||||
into new ancient_backup, then delete the original ancientdb dir and rename the ancient_backup to original one for replacement,
|
||||
finally assemble the statedb and new ancientDb together.
|
||||
The purpose of doing it is because the block data will be moved into the ancient store when it
|
||||
@@ -244,7 +247,16 @@ func accessDb(ctx *cli.Context, stack *node.Node) (ethdb.Database, error) {
|
||||
NoBuild: true,
|
||||
AsyncBuild: false,
|
||||
}
|
||||
snaptree, err := snapshot.New(snapconfig, chaindb, trie.NewDatabase(chaindb, nil), headBlock.Root(), TriesInMemory, false)
|
||||
dbScheme := rawdb.ReadStateScheme(chaindb)
|
||||
var config *triedb.Config
|
||||
if dbScheme == rawdb.PathScheme {
|
||||
config = &triedb.Config{
|
||||
PathDB: utils.PathDBConfigAddJournalFilePath(stack, pathdb.ReadOnly),
|
||||
}
|
||||
} else if dbScheme == rawdb.HashScheme {
|
||||
config = triedb.HashDefaults
|
||||
}
|
||||
snaptree, err := snapshot.New(snapconfig, chaindb, triedb.NewDatabase(chaindb, config), headBlock.Root(), TriesInMemory, false)
|
||||
if err != nil {
|
||||
log.Error("snaptree error", "err", err)
|
||||
return nil, err // The relevant snapshot(s) might not exist
|
||||
@@ -332,6 +344,9 @@ func pruneBlock(ctx *cli.Context) error {
|
||||
stack, config = makeConfigNode(ctx)
|
||||
defer stack.Close()
|
||||
blockAmountReserved = ctx.Uint64(utils.BlockAmountReserved.Name)
|
||||
if blockAmountReserved < params.FullImmutabilityThreshold {
|
||||
return fmt.Errorf("block-amount-reserved must be greater than or equal to %d", params.FullImmutabilityThreshold)
|
||||
}
|
||||
chaindb, err = accessDb(ctx, stack)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -354,7 +369,15 @@ func pruneBlock(ctx *cli.Context) error {
|
||||
if !ctx.IsSet(utils.AncientFlag.Name) {
|
||||
return errors.New("datadir.ancient must be set")
|
||||
} else {
|
||||
oldAncientPath = ctx.String(utils.AncientFlag.Name)
|
||||
if stack.CheckIfMultiDataBase() {
|
||||
ancientPath := ctx.String(utils.AncientFlag.Name)
|
||||
index := strings.LastIndex(ancientPath, "/ancient/chain")
|
||||
if index != -1 {
|
||||
oldAncientPath = ancientPath[:index] + "/block/ancient/chain"
|
||||
}
|
||||
} else {
|
||||
oldAncientPath = ctx.String(utils.AncientFlag.Name)
|
||||
}
|
||||
if !filepath.IsAbs(oldAncientPath) {
|
||||
// force absolute paths, which often fail due to the splicing of relative paths
|
||||
return errors.New("datadir.ancient not abs path")
|
||||
@@ -400,7 +423,7 @@ func pruneBlock(ctx *cli.Context) error {
|
||||
}
|
||||
|
||||
if _, err := os.Stat(newAncientPath); err == nil {
|
||||
// No file lock found for old ancientDB but new ancientDB exsisted, indicating the geth was interrupted
|
||||
// No file lock found for old ancientDB but new ancientDB existed, indicating the geth was interrupted
|
||||
// after old ancientDB removal, this happened after backup successfully, so just rename the new ancientDB
|
||||
if err := blockpruner.AncientDbReplacer(); err != nil {
|
||||
log.Error("Failed to rename new ancient directory")
|
||||
@@ -436,13 +459,15 @@ func pruneState(ctx *cli.Context) error {
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, false, false)
|
||||
defer chaindb.Close()
|
||||
|
||||
if rawdb.ReadStateScheme(chaindb) != rawdb.HashScheme {
|
||||
log.Crit("Offline pruning is not required for path scheme")
|
||||
}
|
||||
prunerconfig := pruner.Config{
|
||||
Datadir: stack.ResolvePath(""),
|
||||
BloomSize: ctx.Uint64(utils.BloomFilterSizeFlag.Name),
|
||||
}
|
||||
|
||||
if rawdb.ReadStateScheme(chaindb) != rawdb.HashScheme {
|
||||
log.Crit("Offline pruning is not required for path scheme")
|
||||
}
|
||||
|
||||
pruner, err := pruner.NewPruner(chaindb, prunerconfig, ctx.Uint64(utils.TriesInMemoryFlag.Name))
|
||||
if err != nil {
|
||||
log.Error("Failed to open snapshot tree", "err", err)
|
||||
@@ -521,7 +546,7 @@ func verifyState(ctx *cli.Context) error {
|
||||
log.Error("Failed to load head block")
|
||||
return errors.New("no head block")
|
||||
}
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
snapConfig := snapshot.Config{
|
||||
@@ -576,7 +601,7 @@ func traverseState(ctx *cli.Context) error {
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||
@@ -685,7 +710,7 @@ func traverseRawState(ctx *cli.Context) error {
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||
@@ -849,7 +874,8 @@ func dumpState(ctx *cli.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
||||
defer db.Close()
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, db, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
snapConfig := snapshot.Config{
|
||||
@@ -932,7 +958,7 @@ func snapshotExportPreimages(ctx *cli.Context) error {
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack, true, false)
|
||||
defer chaindb.Close()
|
||||
|
||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||
triedb := utils.MakeTrieDatabase(ctx, stack, chaindb, false, true, false)
|
||||
defer triedb.Close()
|
||||
|
||||
var root common.Hash
|
||||
|
||||
@@ -11,6 +11,7 @@ Install node.js dependency:
|
||||
npm install
|
||||
```
|
||||
## Run
|
||||
### 1.Get Validator's Information: Version, MinGasPrice
|
||||
mainnet validators version
|
||||
```bash
|
||||
npm run startMainnet
|
||||
@@ -19,7 +20,28 @@ testnet validators version
|
||||
```bash
|
||||
npm run startTestnet
|
||||
```
|
||||
Transaction count
|
||||
|
||||
### 2.Get Transaction Count
|
||||
```bash
|
||||
node gettxcount.js --rpc ${url} --startNum ${start} --endNum ${end} --miner ${miner} (optional)
|
||||
```
|
||||
```
|
||||
|
||||
### 3. Get Performance
|
||||
```bash
|
||||
node get_perf.js --rpc ${url} --startNum ${start} --endNum ${end}
|
||||
```
|
||||
output as following
|
||||
```bash
|
||||
Get the performance between [ 19470 , 19670 )
|
||||
txCountPerBlock = 3142.81 txCountTotal = 628562 BlockCount = 200 avgBlockTime = 3.005 inturnBlocksRatio = 0.975 justifiedBlocksRatio = 0.98
|
||||
txCountPerSecond = 1045.8602329450914 avgGasUsedPerBlock = 250.02062627 avgGasUsedPerSecond = 83.20153952412646
|
||||
```
|
||||
|
||||
### 4. Get validators slash count
|
||||
```bash
|
||||
use the latest block
|
||||
node getslashcount.js --Rpc ${ArchiveRpc}
|
||||
use a block number
|
||||
node getslashcount.js --Rpc ${ArchiveRpc} --Num ${blockNum}
|
||||
```
|
||||
|
||||
|
||||
51
cmd/jsutils/check_blobtx.js
Normal file
51
cmd/jsutils/check_blobtx.js
Normal file
@@ -0,0 +1,51 @@
|
||||
import { ethers } from "ethers";
|
||||
import program from "commander";
|
||||
|
||||
// depends on ethjs v6.11.0+ for 4844, https://github.com/ethers-io/ethers.js/releases/tag/v6.11.0
|
||||
// BSC testnet enabled 4844 on block: 39539137
|
||||
// Usage:
|
||||
// nvm use 20
|
||||
// node check_blobtx.js --rpc https://data-seed-prebsc-1-s1.binance.org:8545 --startNum 39539137
|
||||
// node check_blobtx.js --rpc https://data-seed-prebsc-1-s1.binance.org:8545 --startNum 39539137 --endNum 40345994
|
||||
program.option("--rpc <Rpc>", "Rpc Server URL");
|
||||
program.option("--startNum <Num>", "start block", 0);
|
||||
program.option("--endNum <Num>", "end block", 0);
|
||||
program.parse(process.argv);
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(program.rpc);
|
||||
const main = async () => {
|
||||
var startBlock = parseInt(program.startNum)
|
||||
var endBlock = parseInt(program.endNum)
|
||||
if (isNaN(endBlock) || isNaN(startBlock) || startBlock == 0) {
|
||||
console.error("invalid input, --startNum", program.startNum, "--end", program.endNum)
|
||||
return
|
||||
}
|
||||
// if --endNum is not specified, set it to the latest block number.
|
||||
if (endBlock == 0) {
|
||||
endBlock = await provider.getBlockNumber();
|
||||
}
|
||||
if (startBlock > endBlock) {
|
||||
console.error("invalid input, startBlock:",startBlock, " endBlock:", endBlock);
|
||||
return
|
||||
}
|
||||
|
||||
for (let i = startBlock; i <= endBlock; i++) {
|
||||
let blockData = await provider.getBlock(i);
|
||||
console.log("startBlock:",startBlock, "endBlock:", endBlock, "curBlock", i, "blobGasUsed", blockData.blobGasUsed);
|
||||
if (blockData.blobGasUsed == 0) {
|
||||
continue
|
||||
}
|
||||
for (let txIndex = 0; txIndex<= blockData.transactions.length - 1; txIndex++) {
|
||||
let txHash = blockData.transactions[txIndex]
|
||||
let txData = await provider.getTransaction(txHash);
|
||||
if (txData.type == 3) {
|
||||
console.log("BlobTx in block:",i, " txIndex:", txIndex, " txHash:", txHash);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
main().then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
49
cmd/jsutils/faucet_request.js
Normal file
49
cmd/jsutils/faucet_request.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import { ethers } from "ethers";
|
||||
import program from "commander";
|
||||
|
||||
// Usage:
|
||||
// node faucet_request.js --rpc localhost:8545 --startNum 39539137
|
||||
// node faucet_request.js --rpc localhost:8545 --startNum 39539137 --endNum 40345994
|
||||
|
||||
// node faucet_request.js --rpc https://data-seed-prebsc-1-s1.bnbchain.org:8545 --startNum 39539137 --endNum 40345994
|
||||
program.option("--rpc <Rpc>", "Rpc Server URL");
|
||||
program.option("--startNum <Num>", "start block", 0);
|
||||
program.option("--endNum <Num>", "end block", 0);
|
||||
program.parse(process.argv);
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(program.rpc);
|
||||
const main = async () => {
|
||||
var startBlock = parseInt(program.startNum)
|
||||
var endBlock = parseInt(program.endNum)
|
||||
if (isNaN(endBlock) || isNaN(startBlock) || startBlock == 0) {
|
||||
console.error("invalid input, --startNum", program.startNum, "--end", program.endNum)
|
||||
return
|
||||
}
|
||||
// if --endNum is not specified, set it to the latest block number.
|
||||
if (endBlock == 0) {
|
||||
endBlock = await provider.getBlockNumber();
|
||||
}
|
||||
if (startBlock > endBlock) {
|
||||
console.error("invalid input, startBlock:",startBlock, " endBlock:", endBlock);
|
||||
return
|
||||
}
|
||||
|
||||
let startBalance = await provider.getBalance("0xaa25Aa7a19f9c426E07dee59b12f944f4d9f1DD3", startBlock)
|
||||
let endBalance = await provider.getBalance("0xaa25Aa7a19f9c426E07dee59b12f944f4d9f1DD3", endBlock)
|
||||
const faucetAmount = BigInt(0.3 * 10**18); // Convert 0.3 ether to wei as a BigInt
|
||||
const numFaucetRequest = (startBalance - endBalance) / faucetAmount;
|
||||
|
||||
// Convert BigInt to ether
|
||||
const startBalanceEth = Number(startBalance) / 10**18;
|
||||
const endBalanceEth = Number(endBalance) / 10**18;
|
||||
|
||||
console.log(`Start Balance: ${startBalanceEth} ETH`);
|
||||
console.log(`End Balance: ${endBalanceEth} ETH`);
|
||||
|
||||
console.log("successful faucet request: ",numFaucetRequest);
|
||||
};
|
||||
main().then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
68
cmd/jsutils/get_perf.js
Normal file
68
cmd/jsutils/get_perf.js
Normal file
@@ -0,0 +1,68 @@
|
||||
import { ethers } from "ethers";
|
||||
import program from "commander";
|
||||
|
||||
program.option("--rpc <rpc>", "Rpc");
|
||||
program.option("--startNum <startNum>", "start num")
|
||||
program.option("--endNum <endNum>", "end num")
|
||||
program.parse(process.argv);
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(program.rpc)
|
||||
|
||||
const main = async () => {
|
||||
let txCountTotal = 0;
|
||||
let gasUsedTotal = 0;
|
||||
let inturnBlocks = 0;
|
||||
let justifiedBlocks = 0;
|
||||
for (let i = program.startNum; i < program.endNum; i++) {
|
||||
let txCount = await provider.send("eth_getBlockTransactionCountByNumber", [
|
||||
ethers.toQuantity(i)]);
|
||||
txCountTotal += ethers.toNumber(txCount)
|
||||
|
||||
let header = await provider.send("eth_getHeaderByNumber", [
|
||||
ethers.toQuantity(i)]);
|
||||
let gasUsed = eval(eval(header.gasUsed).toString(10))
|
||||
gasUsedTotal += gasUsed
|
||||
let difficulty = eval(eval(header.difficulty).toString(10))
|
||||
if (difficulty == 2) {
|
||||
inturnBlocks += 1
|
||||
}
|
||||
let timestamp = eval(eval(header.timestamp).toString(10))
|
||||
|
||||
let justifiedNumber = await provider.send("parlia_getJustifiedNumber", [
|
||||
ethers.toQuantity(i)]);
|
||||
if (justifiedNumber + 1 == i) {
|
||||
justifiedBlocks += 1
|
||||
} else {
|
||||
console.log("justified unexpected", "BlockNumber =", i,"justifiedNumber",justifiedNumber)
|
||||
}
|
||||
console.log("BlockNumber =", i, "mod =", i%4, "miner =", header.miner , "difficulty =", difficulty, "txCount =", ethers.toNumber(txCount), "gasUsed", gasUsed, "timestamp", timestamp)
|
||||
}
|
||||
|
||||
let blockCount = program.endNum - program.startNum
|
||||
let txCountPerBlock = txCountTotal/blockCount
|
||||
|
||||
let startHeader = await provider.send("eth_getHeaderByNumber", [
|
||||
ethers.toQuantity(program.startNum)]);
|
||||
let startTime = eval(eval(startHeader.timestamp).toString(10))
|
||||
let endHeader = await provider.send("eth_getHeaderByNumber", [
|
||||
ethers.toQuantity(program.endNum)]);
|
||||
let endTime = eval(eval(endHeader.timestamp).toString(10))
|
||||
let timeCost = endTime - startTime
|
||||
let avgBlockTime = timeCost/blockCount
|
||||
let inturnBlocksRatio = inturnBlocks/blockCount
|
||||
let justifiedBlocksRatio = justifiedBlocks/blockCount
|
||||
let tps = txCountTotal/timeCost
|
||||
let M = 1000000
|
||||
let avgGasUsedPerBlock = gasUsedTotal/blockCount/M
|
||||
let avgGasUsedPerSecond = gasUsedTotal/timeCost/M
|
||||
|
||||
console.log("Get the performance between [", program.startNum, ",", program.endNum, ")");
|
||||
console.log("txCountPerBlock =", txCountPerBlock, "txCountTotal =", txCountTotal, "BlockCount =", blockCount, "avgBlockTime =", avgBlockTime, "inturnBlocksRatio =", inturnBlocksRatio, "justifiedBlocksRatio =", justifiedBlocksRatio);
|
||||
console.log("txCountPerSecond =", tps, "avgGasUsedPerBlock =", avgGasUsedPerBlock, "avgGasUsedPerSecond =", avgGasUsedPerSecond);
|
||||
};
|
||||
|
||||
main().then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
119
cmd/jsutils/getslashcount.js
Normal file
119
cmd/jsutils/getslashcount.js
Normal file
@@ -0,0 +1,119 @@
|
||||
import { ethers } from "ethers";
|
||||
import program from "commander";
|
||||
|
||||
program.option("--Rpc <Rpc>", "Rpc");
|
||||
program.option("--Num <Num>", "num", 0)
|
||||
program.parse(process.argv);
|
||||
|
||||
const provider = new ethers.JsonRpcProvider(program.Rpc);
|
||||
|
||||
const slashAbi = [
|
||||
"function getSlashIndicator(address validatorAddr) external view returns (uint256, uint256)"
|
||||
]
|
||||
const validatorSetAbi = [
|
||||
"function getLivingValidators() external view returns (address[], bytes[])"
|
||||
]
|
||||
const stakeHubAbi = [
|
||||
"function getValidatorDescription(address validatorAddr) external view returns (tuple(string, string, string, string))",
|
||||
"function consensusToOperator(address consensusAddr) public view returns (address)"
|
||||
]
|
||||
const addrValidatorSet = '0x0000000000000000000000000000000000001000';
|
||||
const validatorSet = new ethers.Contract(addrValidatorSet, validatorSetAbi, provider);
|
||||
|
||||
const addrSlash = '0x0000000000000000000000000000000000001001';
|
||||
const slashIndicator = new ethers.Contract(addrSlash, slashAbi, provider)
|
||||
|
||||
const addrStakeHub = '0x0000000000000000000000000000000000002002';
|
||||
const stakeHub = new ethers.Contract(addrStakeHub, stakeHubAbi, provider)
|
||||
|
||||
const validatorMap = new Map([
|
||||
//BSC
|
||||
["0x37e9627A91DD13e453246856D58797Ad6583D762", "LegendII"],
|
||||
["0xB4647b856CB9C3856d559C885Bed8B43e0846a47", "CertiK"],
|
||||
["0x75B851a27D7101438F45fce31816501193239A83", "Figment"],
|
||||
["0x502aECFE253E6AA0e8D2A06E12438FFeD0Fe16a0", "BscScan"],
|
||||
["0xCa503a7eD99eca485da2E875aedf7758472c378C", "InfStones"],
|
||||
["0x5009317FD4F6F8FeEa9dAe41E5F0a4737BB7A7D5", "NodeReal"],
|
||||
["0x1cFDBd2dFf70C6e2e30df5012726F87731F38164", "Tranchess"],
|
||||
["0xF8de5e61322302b2c6e0a525cC842F10332811bf", "Namelix"],
|
||||
["0xCcB42A9b8d6C46468900527Bc741938E78AB4577", "Turing"],
|
||||
["0x9f1b7FAE54BE07F4FEE34Eb1aaCb39A1F7B6FC92", "TWStaking"],
|
||||
["0x7E1FdF03Eb3aC35BF0256694D7fBe6B6d7b3E0c8","LegendIII"],
|
||||
["0x7b501c7944185130DD4aD73293e8Aa84eFfDcee7","MathW"],
|
||||
["0x58567F7A51a58708C8B40ec592A38bA64C0697De","Legend"],
|
||||
["0x460A252B4fEEFA821d3351731220627D7B7d1F3d","Defibit"],
|
||||
["0x8A239732871AdC8829EA2f47e94087C5FBad47b6","The48Club"],
|
||||
["0xD3b0d838cCCEAe7ebF1781D11D1bB741DB7Fe1A7","BNBEve"],
|
||||
["0xF8B99643fAfC79d9404DE68E48C4D49a3936f787","Avengers"],
|
||||
["0x4e5acf9684652BEa56F2f01b7101a225Ee33d23f","HashKey"],
|
||||
["0x9bb56C2B4DBE5a06d79911C9899B6f817696ACFc","Feynman"],
|
||||
["0xbdcc079BBb23C1D9a6F36AA31309676C258aBAC7","Fuji"],
|
||||
["0x38944092685a336CB6B9ea58836436709a2adC89","Shannon"],
|
||||
["0xfC1004C0f296Ec3Df4F6762E9EabfcF20EB304a2","Aoraki"],
|
||||
["0xa0884bb00E5F23fE2427f0E5eC9E51F812848563","Coda"],
|
||||
["0xe7776De78740f28a96412eE5cbbB8f90896b11A5","Ankr"],
|
||||
["0xA2D969E82524001Cb6a2357dBF5922B04aD2FCD8","Pexmons"],
|
||||
["0x5cf810AB8C718ac065b45f892A5BAdAB2B2946B9","Zen"],
|
||||
["0x4d15D9BCd0c2f33E7510c0de8b42697CA558234a","LegendVII"],
|
||||
["0x1579ca96EBd49A0B173f86C372436ab1AD393380","LegendV"],
|
||||
["0xd1F72d433f362922f6565FC77c25e095B29141c8","LegendVI"],
|
||||
["0xf9814D93b4d904AaA855cBD4266D6Eb0Ec1Aa478","Legend8"],
|
||||
["0x025a4e09Ea947b8d695f53ddFDD48ddB8F9B06b7","Ciscox"],
|
||||
["0xE9436F6F30b4B01b57F2780B2898f3820EbD7B98","LegendIV"],
|
||||
["0xC2d534F079444E6E7Ff9DabB3FD8a26c607932c8","Axion"],
|
||||
["0x9F7110Ba7EdFda83Fc71BeA6BA3c0591117b440D","LegendIX"],
|
||||
["0xB997Bf1E3b96919fBA592c1F61CE507E165Ec030","Seoraksan"],
|
||||
["0x286C1b674d48cFF67b4096b6c1dc22e769581E91","Sigm8"],
|
||||
["0x73A26778ef9509a6E94b55310eE7233795a9EB25","Coinlix"],
|
||||
["0x18c44f4FBEde9826C7f257d500A65a3D5A8edebc","Nozti"],
|
||||
["0xA100FCd08cE722Dc68Ddc3b54237070Cb186f118","Tiollo"],
|
||||
["0x0F28847cfdbf7508B13Ebb9cEb94B2f1B32E9503","Raptas"],
|
||||
["0xfD85346c8C991baC16b9c9157e6bdfDACE1cD7d7","Glorin"],
|
||||
["0x978F05CED39A4EaFa6E8FD045Fe2dd6Da836c7DF","NovaX"],
|
||||
["0xd849d1dF66bFF1c2739B4399425755C2E0fAbbAb","Nexa"],
|
||||
["0xA015d9e9206859c13201BB3D6B324d6634276534","Star"],
|
||||
["0x5ADde0151BfAB27f329e5112c1AeDeed7f0D3692","Veri"],
|
||||
//Chapel
|
||||
["0x08265dA01E1A65d62b903c7B34c08cB389bF3D99","Ararat"],
|
||||
["0x7f5f2cF1aec83bF0c74DF566a41aa7ed65EA84Ea","Kita"],
|
||||
["0x53387F3321FD69d1E030BB921230dFb188826AFF","Fuji"],
|
||||
["0x76D76ee8823dE52A1A431884c2ca930C5e72bff3","Seoraksan"],
|
||||
["0xd447b49CD040D20BC21e49ffEa6487F5638e4346","Everest"],
|
||||
["0x1a3d9D7A717D64e6088aC937d5aAcDD3E20ca963","Elbrus"],
|
||||
["0x40D3256EB0BaBE89f0ea54EDAa398513136612f5","Bloxroute"],
|
||||
["0xF9a1Db0d6f22Bd78ffAECCbc8F47c83Df9FBdbCf","Test"]
|
||||
]);
|
||||
|
||||
|
||||
const main = async () => {
|
||||
let blockNum = ethers.getNumber(program.Num)
|
||||
if (blockNum === 0) {
|
||||
blockNum = await provider.getBlockNumber()
|
||||
}
|
||||
let block = await provider.getBlock(blockNum)
|
||||
console.log("At block", blockNum, "time", block.date)
|
||||
const data = await validatorSet.getLivingValidators({blockTag:blockNum})
|
||||
let totalSlash = 0
|
||||
for (let i = 0; i < data[0].length; i++) {
|
||||
let addr = data[0][i];
|
||||
var val
|
||||
if (!validatorMap.has(addr)) {
|
||||
let opAddr = await stakeHub.consensusToOperator(addr, {blockTag:blockNum})
|
||||
let value = await stakeHub.getValidatorDescription(opAddr, {blockTag:blockNum})
|
||||
val = value[0]
|
||||
console.log(addr, val)
|
||||
} else {
|
||||
val = validatorMap.get(addr)
|
||||
}
|
||||
let info = await slashIndicator.getSlashIndicator(addr, {blockTag:blockNum})
|
||||
let count = ethers.toNumber(info[1])
|
||||
totalSlash += count
|
||||
console.log("Slash:", count, addr, val)
|
||||
}
|
||||
console.log("Total slash count", totalSlash)
|
||||
};
|
||||
main().then(() => process.exit(0))
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -4,6 +4,9 @@ import program from "commander";
|
||||
program.option("--rpc <rpc>", "Rpc");
|
||||
program.option("--startNum <startNum>", "start num")
|
||||
program.option("--endNum <endNum>", "end num")
|
||||
// --miner:
|
||||
// specified: find the max txCounter from the specified validator
|
||||
// not specified: find the max txCounter from all validators
|
||||
program.option("--miner <miner>", "miner", "")
|
||||
program.parse(process.argv);
|
||||
|
||||
|
||||
@@ -12,10 +12,23 @@ const main = async () => {
|
||||
console.log(blockNum);
|
||||
for (let i = 0; i < program.Num; i++) {
|
||||
let blockData = await provider.getBlock(blockNum - i);
|
||||
// 1.get Geth client version
|
||||
let major = ethers.toNumber(ethers.dataSlice(blockData.extraData, 2, 3))
|
||||
let minor = ethers.toNumber(ethers.dataSlice(blockData.extraData, 3, 4))
|
||||
let patch = ethers.toNumber(ethers.dataSlice(blockData.extraData, 4, 5))
|
||||
console.log(blockData.miner, "version =", major + "." + minor + "." + patch)
|
||||
|
||||
// 2.get minimum txGasPrice based on the last non-zero-gasprice transaction
|
||||
let lastGasPrice = 0
|
||||
for (let txIndex = blockData.transactions.length - 1; txIndex >= 0; txIndex--) {
|
||||
let txHash = blockData.transactions[txIndex]
|
||||
let txData = await provider.getTransaction(txHash);
|
||||
if (txData.gasPrice == 0) {
|
||||
continue
|
||||
}
|
||||
lastGasPrice = txData.gasPrice
|
||||
break
|
||||
}
|
||||
console.log(blockData.miner, "version =", major + "." + minor + "." + patch, " MinGasPrice = " + lastGasPrice)
|
||||
}
|
||||
};
|
||||
main().then(() => process.exit(0))
|
||||
|
||||
191
cmd/utils/cmd.go
191
cmd/utils/cmd.go
@@ -19,12 +19,15 @@ package utils
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -39,8 +42,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/eth/ethconfig"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"github.com/ethereum/go-ethereum/internal/era"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
@@ -228,6 +233,105 @@ func ImportChain(chain *core.BlockChain, fn string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func readList(filename string) ([]string, error) {
|
||||
b, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return strings.Split(string(b), "\n"), nil
|
||||
}
|
||||
|
||||
// ImportHistory imports Era1 files containing historical block information,
|
||||
// starting from genesis.
|
||||
func ImportHistory(chain *core.BlockChain, db ethdb.Database, dir string, network string) error {
|
||||
if chain.CurrentSnapBlock().Number.BitLen() != 0 {
|
||||
return fmt.Errorf("history import only supported when starting from genesis")
|
||||
}
|
||||
entries, err := era.ReadDir(dir, network)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading %s: %w", dir, err)
|
||||
}
|
||||
checksums, err := readList(path.Join(dir, "checksums.txt"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to read checksums.txt: %w", err)
|
||||
}
|
||||
if len(checksums) != len(entries) {
|
||||
return fmt.Errorf("expected equal number of checksums and entries, have: %d checksums, %d entries", len(checksums), len(entries))
|
||||
}
|
||||
var (
|
||||
start = time.Now()
|
||||
reported = time.Now()
|
||||
imported = 0
|
||||
forker = core.NewForkChoice(chain, nil)
|
||||
h = sha256.New()
|
||||
buf = bytes.NewBuffer(nil)
|
||||
)
|
||||
for i, filename := range entries {
|
||||
err := func() error {
|
||||
f, err := os.Open(path.Join(dir, filename))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to open era: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
// Validate checksum.
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return fmt.Errorf("unable to recalculate checksum: %w", err)
|
||||
}
|
||||
if have, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; have != want {
|
||||
return fmt.Errorf("checksum mismatch: have %s, want %s", have, want)
|
||||
}
|
||||
h.Reset()
|
||||
buf.Reset()
|
||||
|
||||
// Import all block data from Era1.
|
||||
e, err := era.From(f)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error opening era: %w", err)
|
||||
}
|
||||
it, err := era.NewIterator(e)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error making era reader: %w", err)
|
||||
}
|
||||
for it.Next() {
|
||||
block, err := it.Block()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading block %d: %w", it.Number(), err)
|
||||
}
|
||||
if block.Number().BitLen() == 0 {
|
||||
continue // skip genesis
|
||||
}
|
||||
receipts, err := it.Receipts()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error reading receipts %d: %w", it.Number(), err)
|
||||
}
|
||||
if status, err := chain.HeaderChain().InsertHeaderChain([]*types.Header{block.Header()}, start, forker); err != nil {
|
||||
return fmt.Errorf("error inserting header %d: %w", it.Number(), err)
|
||||
} else if status != core.CanonStatTy {
|
||||
return fmt.Errorf("error inserting header %d, not canon: %v", it.Number(), status)
|
||||
}
|
||||
if _, err := chain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{receipts}, 2^64-1); err != nil {
|
||||
return fmt.Errorf("error inserting body %d: %w", it.Number(), err)
|
||||
}
|
||||
imported += 1
|
||||
|
||||
// Give the user some feedback that something is happening.
|
||||
if time.Since(reported) >= 8*time.Second {
|
||||
log.Info("Importing Era files", "head", it.Number(), "imported", imported, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
imported = 0
|
||||
reported = time.Now()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block {
|
||||
head := chain.CurrentBlock()
|
||||
for i, block := range blocks {
|
||||
@@ -297,6 +401,93 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExportHistory exports blockchain history into the specified directory,
|
||||
// following the Era format.
|
||||
func ExportHistory(bc *core.BlockChain, dir string, first, last, step uint64) error {
|
||||
log.Info("Exporting blockchain history", "dir", dir)
|
||||
if head := bc.CurrentBlock().Number.Uint64(); head < last {
|
||||
log.Warn("Last block beyond head, setting last = head", "head", head, "last", last)
|
||||
last = head
|
||||
}
|
||||
network := "unknown"
|
||||
if name, ok := params.NetworkNames[bc.Config().ChainID.String()]; ok {
|
||||
network = name
|
||||
}
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf("error creating output directory: %w", err)
|
||||
}
|
||||
var (
|
||||
start = time.Now()
|
||||
reported = time.Now()
|
||||
h = sha256.New()
|
||||
buf = bytes.NewBuffer(nil)
|
||||
checksums []string
|
||||
)
|
||||
for i := first; i <= last; i += step {
|
||||
err := func() error {
|
||||
filename := path.Join(dir, era.Filename(network, int(i/step), common.Hash{}))
|
||||
f, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not create era file: %w", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
w := era.NewBuilder(f)
|
||||
for j := uint64(0); j < step && j <= last-i; j++ {
|
||||
var (
|
||||
n = i + j
|
||||
block = bc.GetBlockByNumber(n)
|
||||
)
|
||||
if block == nil {
|
||||
return fmt.Errorf("export failed on #%d: not found", n)
|
||||
}
|
||||
receipts := bc.GetReceiptsByHash(block.Hash())
|
||||
if receipts == nil {
|
||||
return fmt.Errorf("export failed on #%d: receipts not found", n)
|
||||
}
|
||||
td := bc.GetTd(block.Hash(), block.NumberU64())
|
||||
if td == nil {
|
||||
return fmt.Errorf("export failed on #%d: total difficulty not found", n)
|
||||
}
|
||||
if err := w.Add(block, receipts, td); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
root, err := w.Finalize()
|
||||
if err != nil {
|
||||
return fmt.Errorf("export failed to finalize %d: %w", step/i, err)
|
||||
}
|
||||
// Set correct filename with root.
|
||||
os.Rename(filename, path.Join(dir, era.Filename(network, int(i/step), root)))
|
||||
|
||||
// Compute checksum of entire Era1.
|
||||
if _, err := f.Seek(0, io.SeekStart); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
return fmt.Errorf("unable to calculate checksum: %w", err)
|
||||
}
|
||||
checksums = append(checksums, common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex())
|
||||
h.Reset()
|
||||
buf.Reset()
|
||||
return nil
|
||||
}()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if time.Since(reported) >= 8*time.Second {
|
||||
log.Info("Exporting blocks", "exported", i, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
reported = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
os.WriteFile(path.Join(dir, "checksums.txt"), []byte(strings.Join(checksums, "\n")), os.ModePerm)
|
||||
|
||||
log.Info("Exported blockchain to", "dir", dir)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ImportPreimages imports a batch of exported hash preimages into the database.
|
||||
// It's a part of the deprecated functionality, should be removed in the future.
|
||||
func ImportPreimages(db ethdb.Database, fn string) error {
|
||||
|
||||
@@ -69,9 +69,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
"github.com/fatih/structs"
|
||||
pcsclite "github.com/gballet/go-libpcsclite"
|
||||
gopsutil "github.com/shirou/gopsutil/mem"
|
||||
@@ -93,6 +93,12 @@ var (
|
||||
Value: flags.DirectoryString(node.DefaultDataDir()),
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
MultiDataBaseFlag = &cli.BoolFlag{
|
||||
Name: "multidatabase",
|
||||
Usage: "Enable a separated state and block database, it will be created within two subdirectory called state and block, " +
|
||||
"Users can copy this state or block directory to another directory or disk, and then create a symbolic link to the state directory under the chaindata",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
DirectBroadcastFlag = &cli.BoolFlag{
|
||||
Name: "directbroadcast",
|
||||
Usage: "Enable directly broadcast mined block to all peers",
|
||||
@@ -144,6 +150,12 @@ var (
|
||||
Usage: "Minimum free disk space in MB, once reached triggers auto shut down (default = --cache.gc converted to MB, 0 = disabled)",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
InstanceFlag = &cli.IntFlag{
|
||||
Name: "instance",
|
||||
Usage: "Configures the ports to avoid conflicts when running multiple nodes on the same machine. Maximum is 200. Only applicable for: port, authrpc.port, discovery,port, http.port, ws.port",
|
||||
Value: 1,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
KeyStoreDirFlag = &flags.DirectoryFlag{
|
||||
Name: "keystore",
|
||||
Usage: "Directory for the keystore (default = inside the datadir)",
|
||||
@@ -212,7 +224,7 @@ var (
|
||||
// hbss2pbss command options
|
||||
ForceFlag = &cli.BoolFlag{
|
||||
Name: "force",
|
||||
Usage: "Force convert hbss trie node to pbss trie node. Ingore any metadata",
|
||||
Usage: "Force convert hbss trie node to pbss trie node. Ignore any metadata",
|
||||
Value: false,
|
||||
}
|
||||
// Dump command options.
|
||||
@@ -293,19 +305,9 @@ var (
|
||||
Usage: "Manually specify the Rialto Genesis Hash, to trigger builtin network logic",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideShanghai = &cli.Uint64Flag{
|
||||
Name: "override.shanghai",
|
||||
Usage: "Manually specify the Shanghai fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideKepler = &cli.Uint64Flag{
|
||||
Name: "override.kepler",
|
||||
Usage: "Manually specify the Kepler fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideCancun = &cli.Uint64Flag{
|
||||
Name: "override.cancun",
|
||||
Usage: "Manually specify the Cancun fork timestamp, overriding the bundled setting",
|
||||
OverrideBohr = &cli.Uint64Flag{
|
||||
Name: "override.bohr",
|
||||
Usage: "Manually specify the Bohr fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideVerkle = &cli.Uint64Flag{
|
||||
@@ -313,9 +315,28 @@ var (
|
||||
Usage: "Manually specify the Verkle fork timestamp, overriding the bundled setting",
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideFeynman = &cli.Uint64Flag{
|
||||
Name: "override.feynman",
|
||||
Usage: "Manually specify the Feynman fork timestamp, overriding the bundled setting",
|
||||
OverrideFullImmutabilityThreshold = &cli.Uint64Flag{
|
||||
Name: "override.immutabilitythreshold",
|
||||
Usage: "It is the number of blocks after which a chain segment is considered immutable, only for testing purpose",
|
||||
Value: params.FullImmutabilityThreshold,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideMinBlocksForBlobRequests = &cli.Uint64Flag{
|
||||
Name: "override.minforblobrequest",
|
||||
Usage: "It keeps blob data available for min blocks in local, only for testing purpose",
|
||||
Value: params.MinBlocksForBlobRequests,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideDefaultExtraReserveForBlobRequests = &cli.Uint64Flag{
|
||||
Name: "override.defaultextrareserve",
|
||||
Usage: "It adds more extra time for expired blobs for some request cases, only for testing purpose",
|
||||
Value: params.DefaultExtraReserveForBlobRequests,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
OverrideBreatheBlockInterval = &cli.Uint64Flag{
|
||||
Name: "override.breatheblockinterval",
|
||||
Usage: "It changes the interval between breathe blocks, only for testing purpose",
|
||||
Value: params.BreatheBlockInterval,
|
||||
Category: flags.EthCategory,
|
||||
}
|
||||
SyncModeFlag = &flags.TextMarshalerFlag{
|
||||
@@ -341,6 +362,12 @@ var (
|
||||
Value: false,
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
JournalFileFlag = &cli.BoolFlag{
|
||||
Name: "journalfile",
|
||||
Usage: "Enable using journal file to store the TrieJournal instead of KVDB in pbss (default = false)",
|
||||
Value: false,
|
||||
Category: flags.StateCategory,
|
||||
}
|
||||
StateHistoryFlag = &cli.Uint64Flag{
|
||||
Name: "history.state",
|
||||
Usage: "Number of recent blocks to retain state history for (default = 90,000 blocks, 0 = entire chain)",
|
||||
@@ -852,6 +879,11 @@ var (
|
||||
Usage: "Disables the peer discovery mechanism (manual peer addition)",
|
||||
Category: flags.NetworkingCategory,
|
||||
}
|
||||
PeerFilterPatternsFlag = &cli.StringSliceFlag{
|
||||
Name: "peerfilter",
|
||||
Usage: "Disallow peers connection if peer name matches the given regular expressions",
|
||||
Category: flags.NetworkingCategory,
|
||||
}
|
||||
DiscoveryV4Flag = &cli.BoolFlag{
|
||||
Name: "discovery.v4",
|
||||
Aliases: []string{"discv4"},
|
||||
@@ -1045,6 +1077,7 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
||||
Name: "block-amount-reserved",
|
||||
Usage: "Sets the expected remained amount of blocks for offline block prune",
|
||||
Category: flags.BlockHistoryCategory,
|
||||
Value: params.FullImmutabilityThreshold,
|
||||
}
|
||||
|
||||
CheckSnapshotWithMPT = &cli.BoolFlag{
|
||||
@@ -1094,6 +1127,14 @@ Please note that --` + MetricsHTTPFlag.Name + ` must be set to start the server.
|
||||
Usage: "Path for the voteJournal dir in fast finality feature (default = inside the datadir)",
|
||||
Category: flags.FastFinalityCategory,
|
||||
}
|
||||
|
||||
// Blob setting
|
||||
BlobExtraReserveFlag = &cli.Uint64Flag{
|
||||
Name: "blob.extra-reserve",
|
||||
Usage: "Extra reserve threshold for blob, blob never expires when 0 is set, default 28800",
|
||||
Value: params.DefaultExtraReserveForBlobRequests,
|
||||
Category: flags.MiscCategory,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -1509,6 +1550,9 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
||||
if ctx.IsSet(NoDiscoverFlag.Name) {
|
||||
cfg.NoDiscovery = true
|
||||
}
|
||||
if ctx.IsSet(PeerFilterPatternsFlag.Name) {
|
||||
cfg.PeerFilterPatterns = ctx.StringSlice(PeerFilterPatternsFlag.Name)
|
||||
}
|
||||
|
||||
CheckExclusive(ctx, DiscoveryV4Flag, NoDiscoverFlag)
|
||||
CheckExclusive(ctx, DiscoveryV5Flag, NoDiscoverFlag)
|
||||
@@ -1535,6 +1579,7 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
|
||||
|
||||
// SetNodeConfig applies node-related command line flags to the config.
|
||||
func SetNodeConfig(ctx *cli.Context, cfg *node.Config) {
|
||||
setInstance(ctx, cfg)
|
||||
SetP2PConfig(ctx, &cfg.P2P)
|
||||
setIPC(ctx, cfg)
|
||||
setHTTP(ctx, cfg)
|
||||
@@ -1928,9 +1973,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
if ctx.IsSet(PathDBSyncFlag.Name) {
|
||||
cfg.PathSyncFlush = true
|
||||
}
|
||||
if ctx.IsSet(JournalFileFlag.Name) {
|
||||
cfg.JournalFileEnabled = true
|
||||
}
|
||||
|
||||
if ctx.String(GCModeFlag.Name) == "archive" && cfg.TransactionHistory != 0 {
|
||||
cfg.TransactionHistory = 0
|
||||
log.Warn("Disabled transaction unindexing for archive node")
|
||||
|
||||
cfg.StateScheme = rawdb.HashScheme
|
||||
log.Warn("Forcing hash state-scheme for archive mode")
|
||||
}
|
||||
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheTrieFlag.Name) {
|
||||
cfg.TrieCleanCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheTrieFlag.Name) / 100
|
||||
@@ -1948,6 +2000,16 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
if cfg.TriesVerifyMode.NeedRemoteVerify() {
|
||||
cfg.EnableTrustProtocol = true
|
||||
}
|
||||
// A node without trie is not able to provide snap data, so it should disable snap protocol.
|
||||
if cfg.TriesVerifyMode != core.LocalVerify {
|
||||
log.Info("Automatically disables snap protocol due to verify mode", "mode", cfg.TriesVerifyMode)
|
||||
cfg.DisableSnapProtocol = true
|
||||
}
|
||||
|
||||
if cfg.SyncMode == downloader.SnapSync && cfg.TriesVerifyMode.NoTries() {
|
||||
log.Warn("Only local TriesVerifyMode can support snap sync, resetting to full sync", "mode", cfg.TriesVerifyMode)
|
||||
cfg.SyncMode = downloader.FullSync
|
||||
}
|
||||
}
|
||||
if ctx.IsSet(CacheFlag.Name) || ctx.IsSet(CacheSnapshotFlag.Name) {
|
||||
cfg.SnapshotCache = ctx.Int(CacheFlag.Name) * ctx.Int(CacheSnapshotFlag.Name) / 100
|
||||
@@ -2010,7 +2072,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
}
|
||||
cfg.Genesis = core.DefaultBSCGenesisBlock()
|
||||
SetDNSDiscoveryDefaults(cfg, params.BSCGenesisHash)
|
||||
case ctx.Bool(ChapelFlag.Name):
|
||||
case ctx.Bool(ChapelFlag.Name) || cfg.NetworkId == 97:
|
||||
if !ctx.IsSet(NetworkIdFlag.Name) {
|
||||
cfg.NetworkId = 97
|
||||
}
|
||||
@@ -2079,7 +2141,7 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
if rawdb.ReadCanonicalHash(chaindb, 0) != (common.Hash{}) {
|
||||
cfg.Genesis = nil // fallback to db content
|
||||
|
||||
//validate genesis has PoS enabled in block 0
|
||||
// validate genesis has PoS enabled in block 0
|
||||
genesis, err := core.ReadGenesis(chaindb)
|
||||
if err != nil {
|
||||
Fatalf("Could not read genesis from database: %v", err)
|
||||
@@ -2112,6 +2174,18 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
||||
if err := kzg4844.UseCKZG(ctx.String(CryptoKZGFlag.Name) == "ckzg"); err != nil {
|
||||
Fatalf("Failed to set KZG library implementation to %s: %v", ctx.String(CryptoKZGFlag.Name), err)
|
||||
}
|
||||
|
||||
// blob setting
|
||||
if ctx.IsSet(OverrideDefaultExtraReserveForBlobRequests.Name) {
|
||||
cfg.BlobExtraReserve = ctx.Uint64(OverrideDefaultExtraReserveForBlobRequests.Name)
|
||||
}
|
||||
if ctx.IsSet(BlobExtraReserveFlag.Name) {
|
||||
extraReserve := ctx.Uint64(BlobExtraReserveFlag.Name)
|
||||
if extraReserve > 0 && extraReserve < params.DefaultExtraReserveForBlobRequests {
|
||||
extraReserve = params.DefaultExtraReserveForBlobRequests
|
||||
}
|
||||
cfg.BlobExtraReserve = extraReserve
|
||||
}
|
||||
}
|
||||
|
||||
// SetDNSDiscoveryDefaults configures DNS discovery with the given URL if
|
||||
@@ -2171,7 +2245,7 @@ func EnableBuildInfo(gitCommit, gitDate string) SetupMetricsOption {
|
||||
}
|
||||
}
|
||||
|
||||
func EnableMinerInfo(ctx *cli.Context, minerConfig miner.Config) SetupMetricsOption {
|
||||
func EnableMinerInfo(ctx *cli.Context, minerConfig *miner.Config) SetupMetricsOption {
|
||||
return func() {
|
||||
if ctx.Bool(MiningEnabledFlag.Name) {
|
||||
// register miner info into metrics
|
||||
@@ -2194,10 +2268,13 @@ func RegisterFilterAPI(stack *node.Node, backend ethapi.Backend, ethcfg *ethconf
|
||||
return filterSystem
|
||||
}
|
||||
|
||||
func EnableNodeInfo(poolConfig legacypool.Config) SetupMetricsOption {
|
||||
func EnableNodeInfo(poolConfig *legacypool.Config, nodeInfo *p2p.NodeInfo) SetupMetricsOption {
|
||||
return func() {
|
||||
// register node info into metrics
|
||||
metrics.NewRegisteredLabel("node-info", nil).Mark(map[string]interface{}{
|
||||
"Enode": nodeInfo.Enode,
|
||||
"ENR": nodeInfo.ENR,
|
||||
"ID": nodeInfo.ID,
|
||||
"PriceLimit": poolConfig.PriceLimit,
|
||||
"PriceBump": poolConfig.PriceBump,
|
||||
"AccountSlots": poolConfig.AccountSlots,
|
||||
@@ -2314,6 +2391,13 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly, disableFree
|
||||
chainDb, err = stack.OpenDatabase("lightchaindata", cache, handles, "", readonly)
|
||||
default:
|
||||
chainDb, err = stack.OpenDatabaseWithFreezer("chaindata", cache, handles, ctx.String(AncientFlag.Name), "", readonly, disableFreeze, false, false)
|
||||
// set the separate state database
|
||||
if stack.CheckIfMultiDataBase() && err == nil {
|
||||
stateDiskDb := MakeStateDataBase(ctx, stack, readonly, false)
|
||||
chainDb.SetStateStore(stateDiskDb)
|
||||
blockDb := MakeBlockDatabase(ctx, stack, readonly, false)
|
||||
chainDb.SetBlockStore(blockDb)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
Fatalf("Could not open database: %v", err)
|
||||
@@ -2321,6 +2405,34 @@ func MakeChainDatabase(ctx *cli.Context, stack *node.Node, readonly, disableFree
|
||||
return chainDb
|
||||
}
|
||||
|
||||
// MakeStateDataBase open a separate state database using the flags passed to the client and will hard crash if it fails.
|
||||
func MakeStateDataBase(ctx *cli.Context, stack *node.Node, readonly, disableFreeze bool) ethdb.Database {
|
||||
cache := ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100
|
||||
handles := MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name)) * 90 / 100
|
||||
statediskdb, err := stack.OpenDatabaseWithFreezer("chaindata/state", cache, handles, "", "", readonly, disableFreeze, false, false)
|
||||
if err != nil {
|
||||
Fatalf("Failed to open separate trie database: %v", err)
|
||||
}
|
||||
return statediskdb
|
||||
}
|
||||
|
||||
// MakeBlockDatabase open a separate block database using the flags passed to the client and will hard crash if it fails.
|
||||
func MakeBlockDatabase(ctx *cli.Context, stack *node.Node, readonly, disableFreeze bool) ethdb.Database {
|
||||
cache := ctx.Int(CacheFlag.Name) * ctx.Int(CacheDatabaseFlag.Name) / 100
|
||||
handles := MakeDatabaseHandles(ctx.Int(FDLimitFlag.Name)) / 10
|
||||
blockDb, err := stack.OpenDatabaseWithFreezer("chaindata/block", cache, handles, "", "", readonly, disableFreeze, false, false)
|
||||
if err != nil {
|
||||
Fatalf("Failed to open separate block database: %v", err)
|
||||
}
|
||||
return blockDb
|
||||
}
|
||||
|
||||
func PathDBConfigAddJournalFilePath(stack *node.Node, config *pathdb.Config) *pathdb.Config {
|
||||
path := fmt.Sprintf("%s/%s", stack.ResolvePath("chaindata"), eth.JournalFileName)
|
||||
config.JournalFilePath = path
|
||||
return config
|
||||
}
|
||||
|
||||
// tryMakeReadOnlyDatabase try to open the chain database in read-only mode,
|
||||
// or fallback to write mode if the database is not initialized.
|
||||
//
|
||||
@@ -2463,8 +2575,8 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
|
||||
}
|
||||
|
||||
// MakeTrieDatabase constructs a trie database based on the configured scheme.
|
||||
func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *trie.Database {
|
||||
config := &trie.Config{
|
||||
func MakeTrieDatabase(ctx *cli.Context, stack *node.Node, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *triedb.Database {
|
||||
config := &triedb.Config{
|
||||
Preimages: preimage,
|
||||
IsVerkle: isVerkle,
|
||||
}
|
||||
@@ -2477,14 +2589,15 @@ func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, read
|
||||
// ignore the parameter silently. TODO(rjl493456442)
|
||||
// please config it if read mode is implemented.
|
||||
config.HashDB = hashdb.Defaults
|
||||
return trie.NewDatabase(disk, config)
|
||||
return triedb.NewDatabase(disk, config)
|
||||
}
|
||||
if readOnly {
|
||||
config.PathDB = pathdb.ReadOnly
|
||||
} else {
|
||||
config.PathDB = pathdb.Defaults
|
||||
}
|
||||
return trie.NewDatabase(disk, config)
|
||||
config.PathDB.JournalFilePath = fmt.Sprintf("%s/%s", stack.ResolvePath("chaindata"), eth.JournalFileName)
|
||||
return triedb.NewDatabase(disk, config)
|
||||
}
|
||||
|
||||
// ParseCLIAndConfigStateScheme parses state scheme in CLI and config.
|
||||
@@ -2505,3 +2618,24 @@ func ParseCLIAndConfigStateScheme(cliScheme, cfgScheme string) (string, error) {
|
||||
}
|
||||
return "", fmt.Errorf("incompatible state scheme, CLI: %s, config: %s", cliScheme, cfgScheme)
|
||||
}
|
||||
|
||||
// setInstance configures the port numbers for the given instance.
|
||||
func setInstance(ctx *cli.Context, cfg *node.Config) {
|
||||
if ctx.IsSet(InstanceFlag.Name) {
|
||||
cfg.Instance = ctx.Int(InstanceFlag.Name)
|
||||
}
|
||||
|
||||
if cfg.Instance > 200 {
|
||||
Fatalf("Instance number %d is too high, maximum is 200", cfg.Instance)
|
||||
}
|
||||
|
||||
if cfg.Instance == 1 { // using default ports
|
||||
return
|
||||
}
|
||||
|
||||
cfg.AuthPort = node.DefaultConfig.AuthPort + cfg.Instance*100 - 100
|
||||
cfg.HTTPPort = node.DefaultHTTPPort - cfg.Instance + 1
|
||||
cfg.WSPort = node.DefaultWSPort + cfg.Instance*2 - 2
|
||||
cfg.P2P.ListenAddr = fmt.Sprintf(":%d", node.DefaultListenPort+cfg.Instance-1)
|
||||
cfg.P2P.DiscAddr = fmt.Sprintf(":%d", node.DefaultDiscPort+cfg.Instance-1)
|
||||
}
|
||||
|
||||
185
cmd/utils/history_test.go
Normal file
185
cmd/utils/history_test.go
Normal file
@@ -0,0 +1,185 @@
|
||||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package utils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
"math/big"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/internal/era"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
var (
|
||||
count uint64 = 128
|
||||
step uint64 = 16
|
||||
)
|
||||
|
||||
func TestHistoryImportAndExport(t *testing.T) {
|
||||
var (
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
genesis = &core.Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: types.GenesisAlloc{address: {Balance: big.NewInt(1000000000000000000)}},
|
||||
}
|
||||
signer = types.LatestSigner(genesis.Config)
|
||||
)
|
||||
|
||||
// Generate chain.
|
||||
db, blocks, _ := core.GenerateChainWithGenesis(genesis, ethash.NewFaker(), int(count), func(i int, g *core.BlockGen) {
|
||||
if i == 0 {
|
||||
return
|
||||
}
|
||||
tx, err := types.SignNewTx(key, signer, &types.DynamicFeeTx{
|
||||
ChainID: genesis.Config.ChainID,
|
||||
Nonce: uint64(i - 1),
|
||||
GasTipCap: common.Big0,
|
||||
GasFeeCap: g.PrevBlock(0).BaseFee(),
|
||||
Gas: 50000,
|
||||
To: &common.Address{0xaa},
|
||||
Value: big.NewInt(int64(i)),
|
||||
Data: nil,
|
||||
AccessList: nil,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("error creating tx: %v", err)
|
||||
}
|
||||
g.AddTx(tx)
|
||||
})
|
||||
|
||||
// Initialize BlockChain.
|
||||
chain, err := core.NewBlockChain(db, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize chain: %v", err)
|
||||
}
|
||||
if _, err := chain.InsertChain(blocks); err != nil {
|
||||
t.Fatalf("error inserting chain: %v", err)
|
||||
}
|
||||
|
||||
// Make temp directory for era files.
|
||||
dir, err := os.MkdirTemp("", "history-export-test")
|
||||
if err != nil {
|
||||
t.Fatalf("error creating temp test directory: %v", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
|
||||
// Export history to temp directory.
|
||||
if err := ExportHistory(chain, dir, 0, count, step); err != nil {
|
||||
t.Fatalf("error exporting history: %v", err)
|
||||
}
|
||||
|
||||
// Read checksums.
|
||||
b, err := os.ReadFile(path.Join(dir, "checksums.txt"))
|
||||
if err != nil {
|
||||
t.Fatalf("failed to read checksums: %v", err)
|
||||
}
|
||||
checksums := strings.Split(string(b), "\n")
|
||||
|
||||
// Verify each Era.
|
||||
entries, _ := era.ReadDir(dir, "mainnet")
|
||||
for i, filename := range entries {
|
||||
func() {
|
||||
f, err := os.Open(path.Join(dir, filename))
|
||||
if err != nil {
|
||||
t.Fatalf("error opening era file: %v", err)
|
||||
}
|
||||
var (
|
||||
h = sha256.New()
|
||||
buf = bytes.NewBuffer(nil)
|
||||
)
|
||||
if _, err := io.Copy(h, f); err != nil {
|
||||
t.Fatalf("unable to recalculate checksum: %v", err)
|
||||
}
|
||||
if got, want := common.BytesToHash(h.Sum(buf.Bytes()[:])).Hex(), checksums[i]; got != want {
|
||||
t.Fatalf("checksum %d does not match: got %s, want %s", i, got, want)
|
||||
}
|
||||
e, err := era.From(f)
|
||||
if err != nil {
|
||||
t.Fatalf("error opening era: %v", err)
|
||||
}
|
||||
defer e.Close()
|
||||
it, err := era.NewIterator(e)
|
||||
if err != nil {
|
||||
t.Fatalf("error making era reader: %v", err)
|
||||
}
|
||||
for j := 0; it.Next(); j++ {
|
||||
n := i*int(step) + j
|
||||
if it.Error() != nil {
|
||||
t.Fatalf("error reading block entry %d: %v", n, it.Error())
|
||||
}
|
||||
block, receipts, err := it.BlockAndReceipts()
|
||||
if err != nil {
|
||||
t.Fatalf("error reading block entry %d: %v", n, err)
|
||||
}
|
||||
want := chain.GetBlockByNumber(uint64(n))
|
||||
if want, got := uint64(n), block.NumberU64(); want != got {
|
||||
t.Fatalf("blocks out of order: want %d, got %d", want, got)
|
||||
}
|
||||
if want.Hash() != block.Hash() {
|
||||
t.Fatalf("block hash mismatch %d: want %s, got %s", n, want.Hash().Hex(), block.Hash().Hex())
|
||||
}
|
||||
if got := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); got != want.TxHash() {
|
||||
t.Fatalf("tx hash %d mismatch: want %s, got %s", n, want.TxHash(), got)
|
||||
}
|
||||
if got := types.CalcUncleHash(block.Uncles()); got != want.UncleHash() {
|
||||
t.Fatalf("uncle hash %d mismatch: want %s, got %s", n, want.UncleHash(), got)
|
||||
}
|
||||
if got := types.DeriveSha(receipts, trie.NewStackTrie(nil)); got != want.ReceiptHash() {
|
||||
t.Fatalf("receipt root %d mismatch: want %s, got %s", n, want.ReceiptHash(), got)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Now import Era.
|
||||
freezer := t.TempDir()
|
||||
db2, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), freezer, "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
db2.Close()
|
||||
})
|
||||
|
||||
genesis.MustCommit(db2, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||
imported, err := core.NewBlockChain(db2, nil, genesis, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to initialize chain: %v", err)
|
||||
}
|
||||
if err := ImportHistory(imported, db2, dir, "mainnet"); err != nil {
|
||||
t.Fatalf("failed to import chain: %v", err)
|
||||
}
|
||||
if have, want := imported.CurrentHeader(), chain.CurrentHeader(); have.Hash() != want.Hash() {
|
||||
t.Fatalf("imported chain does not match expected, have (%d, %s) want (%d, %s)", have.Number, have.Hash(), want.Number, want.Hash())
|
||||
}
|
||||
}
|
||||
23
common/bidutil/bidutil.go
Normal file
23
common/bidutil/bidutil.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package bidutil
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// BidBetterBefore returns the time when the next bid better be received, considering the delay and bid simulation.
|
||||
// BidBetterBefore is earlier than BidMustBefore.
|
||||
func BidBetterBefore(parentHeader *types.Header, blockPeriod uint64, delayLeftOver, simulationLeftOver time.Duration) time.Time {
|
||||
nextHeaderTime := BidMustBefore(parentHeader, blockPeriod, delayLeftOver)
|
||||
nextHeaderTime = nextHeaderTime.Add(-simulationLeftOver)
|
||||
return nextHeaderTime
|
||||
}
|
||||
|
||||
// BidMustBefore returns the time when the next bid must be received,
|
||||
// only considering the consensus delay but not bid simulation duration.
|
||||
func BidMustBefore(parentHeader *types.Header, blockPeriod uint64, delayLeftOver time.Duration) time.Time {
|
||||
nextHeaderTime := time.Unix(int64(parentHeader.Time+blockPeriod), 0)
|
||||
nextHeaderTime = nextHeaderTime.Add(-delayLeftOver)
|
||||
return nextHeaderTime
|
||||
}
|
||||
@@ -333,6 +333,11 @@ func (beacon *Beacon) verifyHeaders(chain consensus.ChainHeaderReader, headers [
|
||||
return abort, results
|
||||
}
|
||||
|
||||
// NextInTurnValidator return the next in-turn validator for header
|
||||
func (beacon *Beacon) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) {
|
||||
return common.Address{}, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// Prepare implements consensus.Engine, initializing the difficulty field of a
|
||||
// header to conform to the beacon protocol. The changes are done inline.
|
||||
func (beacon *Beacon) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||
|
||||
@@ -407,7 +407,7 @@ func (c *Clique) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
|
||||
// at a checkpoint block without a parent (light client CHT), or we have piled
|
||||
// up more headers than allowed to be reorged (chain reinit from a freezer),
|
||||
// consider the checkpoint trusted and snapshot it.
|
||||
if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > params.FullImmutabilityThreshold || chain.GetHeaderByNumber(number-1) == nil)) {
|
||||
if number == 0 || (number%c.config.Epoch == 0 && (len(headers) > int(params.FullImmutabilityThreshold) || chain.GetHeaderByNumber(number-1) == nil)) {
|
||||
checkpoint := chain.GetHeaderByNumber(number)
|
||||
if checkpoint != nil {
|
||||
hash := checkpoint.Hash()
|
||||
@@ -511,6 +511,11 @@ func (c *Clique) verifySeal(snap *Snapshot, header *types.Header, parents []*typ
|
||||
return nil
|
||||
}
|
||||
|
||||
// NextInTurnValidator return the next in-turn validator for header
|
||||
func (c *Clique) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) {
|
||||
return common.Address{}, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// Prepare implements consensus.Engine, preparing all the consensus fields of the
|
||||
// header for running the transactions on top.
|
||||
func (c *Clique) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||
|
||||
@@ -48,7 +48,7 @@ func TestReimportMirroredState(t *testing.T) {
|
||||
genspec := &core.Genesis{
|
||||
Config: params.AllCliqueProtocolChanges,
|
||||
ExtraData: make([]byte, extraVanity+common.AddressLength+extraSeal),
|
||||
Alloc: map[common.Address]core.GenesisAccount{
|
||||
Alloc: map[common.Address]types.Account{
|
||||
addr: {Balance: big.NewInt(10000000000000000)},
|
||||
},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
|
||||
@@ -467,7 +467,6 @@ func (tt *cliqueTest) run(t *testing.T) {
|
||||
for j := 0; j < len(batches)-1; j++ {
|
||||
if k, err := chain.InsertChain(batches[j]); err != nil {
|
||||
t.Fatalf("failed to import batch %d, block %d: %v", j, k, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
if _, err = chain.InsertChain(batches[len(batches)-1]); err != tt.failure {
|
||||
|
||||
@@ -58,6 +58,12 @@ type ChainHeaderReader interface {
|
||||
|
||||
// GetHighestVerifiedHeader retrieves the highest header verified.
|
||||
GetHighestVerifiedHeader() *types.Header
|
||||
|
||||
// GetVerifiedBlockByHash retrieves the highest verified block.
|
||||
GetVerifiedBlockByHash(hash common.Hash) *types.Header
|
||||
|
||||
// ChasingHead return the best chain head of peers.
|
||||
ChasingHead() *types.Header
|
||||
}
|
||||
|
||||
type VotePool interface {
|
||||
@@ -94,6 +100,9 @@ type Engine interface {
|
||||
// rules of a given engine.
|
||||
VerifyUncles(chain ChainReader, block *types.Block) error
|
||||
|
||||
// NextInTurnValidator return the next in-turn validator for header
|
||||
NextInTurnValidator(chain ChainHeaderReader, header *types.Header) (common.Address, error)
|
||||
|
||||
// Prepare initializes the consensus fields of a block header according to the
|
||||
// rules of a particular engine. The changes are executed inline.
|
||||
Prepare(chain ChainHeaderReader, header *types.Header) error
|
||||
|
||||
@@ -489,6 +489,11 @@ var FrontierDifficultyCalculator = calcDifficultyFrontier
|
||||
var HomesteadDifficultyCalculator = calcDifficultyHomestead
|
||||
var DynamicDifficultyCalculator = makeDifficultyCalculator
|
||||
|
||||
// NextInTurnValidator return the next in-turn validator for header
|
||||
func (ethash *Ethash) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) {
|
||||
return common.Address{}, errors.New("not implemented")
|
||||
}
|
||||
|
||||
// Prepare implements consensus.Engine, initializing the difficulty field of a
|
||||
// header to conform to the ethash protocol. The changes are done inline.
|
||||
func (ethash *Ethash) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||
|
||||
@@ -31,13 +31,7 @@ type API struct {
|
||||
|
||||
// GetSnapshot retrieves the state snapshot at a given block.
|
||||
func (api *API) GetSnapshot(number *rpc.BlockNumber) (*Snapshot, error) {
|
||||
// Retrieve the requested block number (or current if none requested)
|
||||
var header *types.Header
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = api.chain.CurrentHeader()
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return its snapshot
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
@@ -56,13 +50,7 @@ func (api *API) GetSnapshotAtHash(hash common.Hash) (*Snapshot, error) {
|
||||
|
||||
// GetValidators retrieves the list of validators at the specified block.
|
||||
func (api *API) GetValidators(number *rpc.BlockNumber) ([]common.Address, error) {
|
||||
// Retrieve the requested block number (or current if none requested)
|
||||
var header *types.Header
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = api.chain.CurrentHeader()
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return the validators from its snapshot
|
||||
if header == nil {
|
||||
return nil, errUnknownBlock
|
||||
@@ -86,3 +74,52 @@ func (api *API) GetValidatorsAtHash(hash common.Hash) ([]common.Address, error)
|
||||
}
|
||||
return snap.validators(), nil
|
||||
}
|
||||
|
||||
func (api *API) GetJustifiedNumber(number *rpc.BlockNumber) (uint64, error) {
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return the validators from its snapshot
|
||||
if header == nil {
|
||||
return 0, errUnknownBlock
|
||||
}
|
||||
snap, err := api.parlia.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
||||
if err != nil || snap.Attestation == nil {
|
||||
return 0, err
|
||||
}
|
||||
return snap.Attestation.TargetNumber, nil
|
||||
}
|
||||
|
||||
func (api *API) GetFinalizedNumber(number *rpc.BlockNumber) (uint64, error) {
|
||||
header := api.getHeader(number)
|
||||
// Ensure we have an actually valid block and return the validators from its snapshot
|
||||
if header == nil {
|
||||
return 0, errUnknownBlock
|
||||
}
|
||||
snap, err := api.parlia.snapshot(api.chain, header.Number.Uint64(), header.Hash(), nil)
|
||||
if err != nil || snap.Attestation == nil {
|
||||
return 0, err
|
||||
}
|
||||
return snap.Attestation.SourceNumber, nil
|
||||
}
|
||||
|
||||
func (api *API) getHeader(number *rpc.BlockNumber) (header *types.Header) {
|
||||
currentHeader := api.chain.CurrentHeader()
|
||||
|
||||
if number == nil || *number == rpc.LatestBlockNumber {
|
||||
header = currentHeader // current if none requested
|
||||
} else if *number == rpc.SafeBlockNumber {
|
||||
justifiedNumber, _, err := api.parlia.GetJustifiedNumberAndHash(api.chain, []*types.Header{currentHeader})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
header = api.chain.GetHeaderByNumber(justifiedNumber)
|
||||
} else if *number == rpc.FinalizedBlockNumber {
|
||||
header = api.parlia.GetFinalizedHeader(api.chain, currentHeader)
|
||||
} else if *number == rpc.PendingBlockNumber {
|
||||
return nil // no pending blocks on bsc
|
||||
} else if *number == rpc.EarliestBlockNumber {
|
||||
header = api.chain.GetHeaderByNumber(0)
|
||||
} else {
|
||||
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package parlia
|
||||
import (
|
||||
"container/heap"
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"math"
|
||||
"math/big"
|
||||
|
||||
@@ -15,14 +15,13 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/internal/ethapi"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
const SecondsPerDay uint64 = 86400
|
||||
|
||||
// the params should be two blocks' time(timestamp)
|
||||
func sameDayInUTC(first, second uint64) bool {
|
||||
return first/SecondsPerDay == second/SecondsPerDay
|
||||
return first/params.BreatheBlockInterval == second/params.BreatheBlockInterval
|
||||
}
|
||||
|
||||
func isBreatheBlock(lastBlockTime, blockTime uint64) bool {
|
||||
@@ -159,7 +158,7 @@ func (p *Parlia) getValidatorElectionInfo(blockNr rpc.BlockNumberOrHash) ([]Vali
|
||||
return nil, err
|
||||
}
|
||||
if totalLength.Int64() != int64(len(validators)) || totalLength.Int64() != int64(len(votingPowers)) || totalLength.Int64() != int64(len(voteAddrs)) {
|
||||
return nil, fmt.Errorf("validator length not match")
|
||||
return nil, errors.New("validator length not match")
|
||||
}
|
||||
|
||||
validatorItems := make([]ValidatorItem, len(validators))
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
@@ -16,7 +17,7 @@ import (
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
|
||||
"github.com/prysmaticlabs/prysm/v5/crypto/bls"
|
||||
"github.com/willf/bitset"
|
||||
"golang.org/x/crypto/sha3"
|
||||
|
||||
@@ -48,8 +49,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
inMemorySnapshots = 256 // Number of recent snapshots to keep in memory
|
||||
inMemorySignatures = 4096 // Number of recent block signatures to keep in memory
|
||||
inMemorySnapshots = 256 // Number of recent snapshots to keep in memory
|
||||
inMemorySignatures = 4096 // Number of recent block signatures to keep in memory
|
||||
inMemoryHeaders = 86400 // Number of recent headers to keep in memory for double sign detection,
|
||||
|
||||
checkpointInterval = 1024 // Number of blocks after which to save the snapshot to the database
|
||||
defaultEpochLength = uint64(100) // Default number of blocks of checkpoint to update validatorSet from contract
|
||||
@@ -80,6 +82,7 @@ var (
|
||||
verifyVoteAttestationErrorCounter = metrics.NewRegisteredCounter("parlia/verifyVoteAttestation/error", nil)
|
||||
updateAttestationErrorCounter = metrics.NewRegisteredCounter("parlia/updateAttestation/error", nil)
|
||||
validVotesfromSelfCounter = metrics.NewRegisteredCounter("parlia/VerifyVote/self", nil)
|
||||
doubleSignCounter = metrics.NewRegisteredCounter("parlia/doublesign", nil)
|
||||
|
||||
systemContracts = map[common.Address]bool{
|
||||
common.HexToAddress(systemcontracts.ValidatorContract): true,
|
||||
@@ -216,8 +219,11 @@ type Parlia struct {
|
||||
genesisHash common.Hash
|
||||
db ethdb.Database // Database to store and retrieve snapshot checkpoints
|
||||
|
||||
recentSnaps *lru.ARCCache // Snapshots for recent block to speed up
|
||||
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
||||
recentSnaps *lru.ARCCache // Snapshots for recent block to speed up
|
||||
signatures *lru.ARCCache // Signatures of recent blocks to speed up mining
|
||||
recentHeaders *lru.ARCCache //
|
||||
// Recent headers to check for double signing: key includes block number and miner. value is the block header
|
||||
// If same key's value already exists for different block header roots then double sign is detected
|
||||
|
||||
signer types.Signer
|
||||
|
||||
@@ -263,6 +269,10 @@ func New(
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
recentHeaders, err := lru.NewARC(inMemoryHeaders)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
vABIBeforeLuban, err := abi.JSON(strings.NewReader(validatorSetABIBeforeLuban))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -286,6 +296,7 @@ func New(
|
||||
db: db,
|
||||
ethAPI: ethAPI,
|
||||
recentSnaps: recentSnaps,
|
||||
recentHeaders: recentHeaders,
|
||||
signatures: signatures,
|
||||
validatorSetABIBeforeLuban: vABIBeforeLuban,
|
||||
validatorSetABI: vABI,
|
||||
@@ -297,6 +308,10 @@ func New(
|
||||
return c
|
||||
}
|
||||
|
||||
func (p *Parlia) Period() uint64 {
|
||||
return p.config.Period
|
||||
}
|
||||
|
||||
func (p *Parlia) IsSystemTransaction(tx *types.Transaction, header *types.Header) (bool, error) {
|
||||
// deploy a contract
|
||||
if tx.To() == nil {
|
||||
@@ -435,7 +450,7 @@ func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header
|
||||
return nil
|
||||
}
|
||||
if attestation.Data == nil {
|
||||
return fmt.Errorf("invalid attestation, vote data is nil")
|
||||
return errors.New("invalid attestation, vote data is nil")
|
||||
}
|
||||
if len(attestation.Extra) > types.MaxAttestationExtraLength {
|
||||
return fmt.Errorf("invalid attestation, too large extra length: %d", len(attestation.Extra))
|
||||
@@ -464,7 +479,7 @@ func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header
|
||||
}
|
||||
justifiedBlockNumber, justifiedBlockHash, err := p.GetJustifiedNumberAndHash(chain, headers)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected error when getting the highest justified number and hash")
|
||||
return errors.New("unexpected error when getting the highest justified number and hash")
|
||||
}
|
||||
if sourceNumber != justifiedBlockNumber || sourceHash != justifiedBlockHash {
|
||||
return fmt.Errorf("invalid attestation, source mismatch, expected block: %d, hash: %s; real block: %d, hash: %s",
|
||||
@@ -486,7 +501,7 @@ func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header
|
||||
validators := snap.validators()
|
||||
validatorsBitSet := bitset.From([]uint64{uint64(attestation.VoteAddressSet)})
|
||||
if validatorsBitSet.Count() > uint(len(validators)) {
|
||||
return fmt.Errorf("invalid attestation, vote number larger than validators number")
|
||||
return errors.New("invalid attestation, vote number larger than validators number")
|
||||
}
|
||||
votedAddrs := make([]bls.PublicKey, 0, validatorsBitSet.Count())
|
||||
for index, val := range validators {
|
||||
@@ -503,7 +518,7 @@ func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header
|
||||
|
||||
// The valid voted validators should be no less than 2/3 validators.
|
||||
if len(votedAddrs) < cmath.CeilDiv(len(snap.Validators)*2, 3) {
|
||||
return fmt.Errorf("invalid attestation, not enough validators voted")
|
||||
return errors.New("invalid attestation, not enough validators voted")
|
||||
}
|
||||
|
||||
// Verify the aggregated signature.
|
||||
@@ -512,7 +527,7 @@ func (p *Parlia) verifyVoteAttestation(chain consensus.ChainHeaderReader, header
|
||||
return fmt.Errorf("BLS signature converts failed: %v", err)
|
||||
}
|
||||
if !aggSig.FastAggregateVerify(votedAddrs, attestation.Data.Hash()) {
|
||||
return fmt.Errorf("invalid attestation, signature verify failed")
|
||||
return errors.New("invalid attestation, signature verify failed")
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -583,14 +598,6 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify existence / non-existence of withdrawalsHash.
|
||||
if header.WithdrawalsHash != nil {
|
||||
return fmt.Errorf("invalid withdrawalsHash: have %x, expected nil", header.WithdrawalsHash)
|
||||
}
|
||||
// Verify the existence / non-existence of cancun-specific header fields
|
||||
if header.ParentBeaconRoot != nil {
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
}
|
||||
cancun := chain.Config().IsCancun(header.Number, header.Time)
|
||||
if !cancun {
|
||||
switch {
|
||||
@@ -598,22 +605,27 @@ func (p *Parlia) verifyHeader(chain consensus.ChainHeaderReader, header *types.H
|
||||
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
|
||||
case header.BlobGasUsed != nil:
|
||||
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
|
||||
case header.WithdrawalsHash != nil:
|
||||
return fmt.Errorf("invalid WithdrawalsHash, have %#x, expected nil", header.WithdrawalsHash)
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case !header.EmptyWithdrawalsHash():
|
||||
return errors.New("header has wrong WithdrawalsHash")
|
||||
}
|
||||
if err := eip4844.VerifyEIP4844Header(parent, header); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !cancun && header.ExcessBlobGas != nil {
|
||||
return fmt.Errorf("invalid excessBlobGas: have %d, expected nil", header.ExcessBlobGas)
|
||||
}
|
||||
if !cancun && header.BlobGasUsed != nil {
|
||||
return fmt.Errorf("invalid blobGasUsed: have %d, expected nil", header.BlobGasUsed)
|
||||
}
|
||||
if cancun {
|
||||
if err := eip4844.VerifyEIP4844Header(parent, header); err != nil {
|
||||
return err
|
||||
bohr := chain.Config().IsBohr(header.Number, header.Time)
|
||||
if !bohr {
|
||||
if header.ParentBeaconRoot != nil {
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected nil", header.ParentBeaconRoot)
|
||||
}
|
||||
} else {
|
||||
if header.ParentBeaconRoot == nil || *header.ParentBeaconRoot != (common.Hash{}) {
|
||||
return fmt.Errorf("invalid parentBeaconRoot, have %#x, expected zero hash", header.ParentBeaconRoot)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -709,10 +721,8 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
|
||||
}
|
||||
}
|
||||
|
||||
// If we're at the genesis, snapshot the initial state. Alternatively if we have
|
||||
// piled up more headers than allowed to be reorged (chain reinit from a freezer),
|
||||
// consider the checkpoint trusted and snapshot it.
|
||||
if number == 0 || (number%p.config.Epoch == 0 && (len(headers) > params.FullImmutabilityThreshold/10)) {
|
||||
// If we're at the genesis, snapshot the initial state.
|
||||
if number == 0 {
|
||||
checkpoint := chain.GetHeaderByNumber(number)
|
||||
if checkpoint != nil {
|
||||
// get checkpoint data
|
||||
@@ -726,12 +736,10 @@ func (p *Parlia) snapshot(chain consensus.ChainHeaderReader, number uint64, hash
|
||||
|
||||
// new snapshot
|
||||
snap = newSnapshot(p.config, p.signatures, number, hash, validators, voteAddrs, p.ethAPI)
|
||||
if snap.Number%checkpointInterval == 0 { // snapshot will only be loaded when snap.Number%checkpointInterval == 0
|
||||
if err := snap.store(p.db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash)
|
||||
if err := snap.store(p.db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Info("Stored checkpoint snapshot to disk", "number", number, "hash", hash)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -823,6 +831,17 @@ func (p *Parlia) verifySeal(chain consensus.ChainHeaderReader, header *types.Hea
|
||||
return errCoinBaseMisMatch
|
||||
}
|
||||
|
||||
// check for double sign & add to cache
|
||||
key := proposalKey(*header)
|
||||
preHash, ok := p.recentHeaders.Get(key)
|
||||
if ok && preHash != header.Hash() {
|
||||
doubleSignCounter.Inc(1)
|
||||
log.Warn("DoubleSign detected", " block", header.Number, " miner", header.Coinbase,
|
||||
"hash1", preHash.(common.Hash), "hash2", header.Hash())
|
||||
} else {
|
||||
p.recentHeaders.Add(key, header.Hash())
|
||||
}
|
||||
|
||||
if _, ok := snap.Validators[signer]; !ok {
|
||||
return errUnauthorizedValidator(signer.String())
|
||||
}
|
||||
@@ -904,7 +923,7 @@ func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, head
|
||||
// Prepare vote data
|
||||
justifiedBlockNumber, justifiedBlockHash, err := p.GetJustifiedNumberAndHash(chain, []*types.Header{parent})
|
||||
if err != nil {
|
||||
return fmt.Errorf("unexpected error when getting the highest justified number and hash")
|
||||
return errors.New("unexpected error when getting the highest justified number and hash")
|
||||
}
|
||||
attestation := &types.VoteAttestation{
|
||||
Data: &types.VoteData{
|
||||
@@ -941,7 +960,7 @@ func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, head
|
||||
validatorsBitSet := bitset.From([]uint64{uint64(attestation.VoteAddressSet)})
|
||||
if validatorsBitSet.Count() < uint(len(signatures)) {
|
||||
log.Warn(fmt.Sprintf("assembleVoteAttestation, check VoteAddress Set failed, expected:%d, real:%d", len(signatures), validatorsBitSet.Count()))
|
||||
return fmt.Errorf("invalid attestation, check VoteAddress Set failed")
|
||||
return errors.New("invalid attestation, check VoteAddress Set failed")
|
||||
}
|
||||
|
||||
// Append attestation to header extra field.
|
||||
@@ -960,6 +979,16 @@ func (p *Parlia) assembleVoteAttestation(chain consensus.ChainHeaderReader, head
|
||||
return nil
|
||||
}
|
||||
|
||||
// NextInTurnValidator return the next in-turn validator for header
|
||||
func (p *Parlia) NextInTurnValidator(chain consensus.ChainHeaderReader, header *types.Header) (common.Address, error) {
|
||||
snap, err := p.snapshot(chain, header.Number.Uint64(), header.Hash(), nil)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
|
||||
return snap.inturnValidator(), nil
|
||||
}
|
||||
|
||||
// Prepare implements consensus.Engine, preparing all the consensus fields of the
|
||||
// header for running the transactions on top.
|
||||
func (p *Parlia) Prepare(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||
@@ -1341,30 +1370,30 @@ func (p *Parlia) IsActiveValidatorAt(chain consensus.ChainHeaderReader, header *
|
||||
func (p *Parlia) VerifyVote(chain consensus.ChainHeaderReader, vote *types.VoteEnvelope) error {
|
||||
targetNumber := vote.Data.TargetNumber
|
||||
targetHash := vote.Data.TargetHash
|
||||
header := chain.GetHeaderByHash(targetHash)
|
||||
header := chain.GetVerifiedBlockByHash(targetHash)
|
||||
if header == nil {
|
||||
log.Warn("BlockHeader at current voteBlockNumber is nil", "targetNumber", targetNumber, "targetHash", targetHash)
|
||||
return fmt.Errorf("BlockHeader at current voteBlockNumber is nil")
|
||||
return errors.New("BlockHeader at current voteBlockNumber is nil")
|
||||
}
|
||||
if header.Number.Uint64() != targetNumber {
|
||||
log.Warn("unexpected target number", "expect", header.Number.Uint64(), "real", targetNumber)
|
||||
return fmt.Errorf("target number mismatch")
|
||||
return errors.New("target number mismatch")
|
||||
}
|
||||
|
||||
justifiedBlockNumber, justifiedBlockHash, err := p.GetJustifiedNumberAndHash(chain, []*types.Header{header})
|
||||
if err != nil {
|
||||
log.Error("failed to get the highest justified number and hash", "headerNumber", header.Number, "headerHash", header.Hash())
|
||||
return fmt.Errorf("unexpected error when getting the highest justified number and hash")
|
||||
return errors.New("unexpected error when getting the highest justified number and hash")
|
||||
}
|
||||
if vote.Data.SourceNumber != justifiedBlockNumber || vote.Data.SourceHash != justifiedBlockHash {
|
||||
return fmt.Errorf("vote source block mismatch")
|
||||
return errors.New("vote source block mismatch")
|
||||
}
|
||||
|
||||
number := header.Number.Uint64()
|
||||
snap, err := p.snapshot(chain, number-1, header.ParentHash, nil)
|
||||
if err != nil {
|
||||
log.Error("failed to get the snapshot from consensus", "error", err)
|
||||
return fmt.Errorf("failed to get the snapshot from consensus")
|
||||
return errors.New("failed to get the snapshot from consensus")
|
||||
}
|
||||
|
||||
validators := snap.Validators
|
||||
@@ -1379,7 +1408,7 @@ func (p *Parlia) VerifyVote(chain consensus.ChainHeaderReader, vote *types.VoteE
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf("vote verification failed")
|
||||
return errors.New("vote verification failed")
|
||||
}
|
||||
|
||||
// Authorize injects a private key into the consensus engine to mint new blocks
|
||||
@@ -1573,11 +1602,35 @@ func CalcDifficulty(snap *Snapshot, signer common.Address) *big.Int {
|
||||
return new(big.Int).Set(diffNoTurn)
|
||||
}
|
||||
|
||||
func encodeSigHeaderWithoutVoteAttestation(w io.Writer, header *types.Header, chainId *big.Int) {
|
||||
err := rlp.Encode(w, []interface{}{
|
||||
chainId,
|
||||
header.ParentHash,
|
||||
header.UncleHash,
|
||||
header.Coinbase,
|
||||
header.Root,
|
||||
header.TxHash,
|
||||
header.ReceiptHash,
|
||||
header.Bloom,
|
||||
header.Difficulty,
|
||||
header.Number,
|
||||
header.GasLimit,
|
||||
header.GasUsed,
|
||||
header.Time,
|
||||
header.Extra[:extraVanity], // this will panic if extra is too short, should check before calling encodeSigHeaderWithoutVoteAttestation
|
||||
header.MixDigest,
|
||||
header.Nonce,
|
||||
})
|
||||
if err != nil {
|
||||
panic("can't encode: " + err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// SealHash returns the hash of a block without vote attestation prior to it being sealed.
|
||||
// So it's not the real hash of a block, just used as unique id to distinguish task
|
||||
func (p *Parlia) SealHash(header *types.Header) (hash common.Hash) {
|
||||
hasher := sha3.NewLegacyKeccak256()
|
||||
types.EncodeSigHeaderWithoutVoteAttestation(hasher, header, p.chainConfig.ChainID)
|
||||
encodeSigHeaderWithoutVoteAttestation(hasher, header, p.chainConfig.ChainID)
|
||||
hasher.Sum(hash[:0])
|
||||
return hash
|
||||
}
|
||||
@@ -1838,7 +1891,7 @@ func (p *Parlia) applyTransaction(
|
||||
// within the branch including `headers` and utilizing the latest element as the head.
|
||||
func (p *Parlia) GetJustifiedNumberAndHash(chain consensus.ChainHeaderReader, headers []*types.Header) (uint64, common.Hash, error) {
|
||||
if chain == nil || len(headers) == 0 || headers[len(headers)-1] == nil {
|
||||
return 0, common.Hash{}, fmt.Errorf("illegal chain or header")
|
||||
return 0, common.Hash{}, errors.New("illegal chain or header")
|
||||
}
|
||||
head := headers[len(headers)-1]
|
||||
snap, err := p.snapshot(chain, head.Number.Uint64(), head.Hash(), headers)
|
||||
@@ -1990,16 +2043,19 @@ func applyMessage(
|
||||
chainConfig *params.ChainConfig,
|
||||
chainContext core.ChainContext,
|
||||
) (uint64, error) {
|
||||
// TODO(Nathan): state.Prepare should be called here, now accessList related EIP not affect systemtxs
|
||||
// EIP1153 may cause a critical issue in the future
|
||||
// Create a new context to be used in the EVM environment
|
||||
context := core.NewEVMBlockContext(header, chainContext, nil)
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := vm.NewEVM(context, vm.TxContext{Origin: msg.From(), GasPrice: big.NewInt(0)}, state, chainConfig, vm.Config{})
|
||||
// Apply the transaction to the current state (included in the env)
|
||||
if chainConfig.IsCancun(header.Number, header.Time) {
|
||||
rules := vmenv.ChainConfig().Rules(vmenv.Context.BlockNumber, vmenv.Context.Random != nil, vmenv.Context.Time)
|
||||
state.Prepare(rules, msg.From(), vmenv.Context.Coinbase, msg.To(), vm.ActivePrecompiles(rules), msg.AccessList)
|
||||
}
|
||||
// Increment the nonce for the next transaction
|
||||
state.SetNonce(msg.From(), state.GetNonce(msg.From())+1)
|
||||
|
||||
ret, returnGas, err := vmenv.Call(
|
||||
vm.AccountRef(msg.From()),
|
||||
*msg.To(),
|
||||
@@ -2012,3 +2068,8 @@ func applyMessage(
|
||||
}
|
||||
return msg.Gas() - returnGas, err
|
||||
}
|
||||
|
||||
// proposalKey build a key which is a combination of the block number and the proposer address.
|
||||
func proposalKey(header types.Header) string {
|
||||
return header.ParentHash.String() + header.Coinbase.String()
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
@@ -267,8 +268,19 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
}
|
||||
}
|
||||
snap.Recents[number] = validator
|
||||
snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity])
|
||||
snap.updateAttestation(header, chainConfig, s.config)
|
||||
// change validator set
|
||||
if number > 0 && number%s.config.Epoch == uint64(len(snap.Validators)/2) {
|
||||
epochKey := math.MaxUint64 - header.Number.Uint64()/s.config.Epoch // impossible used as a block number
|
||||
if chainConfig.IsBohr(header.Number, header.Time) {
|
||||
// after switching the validator set, snap.Validators may become larger,
|
||||
// then the unexpected second switch will happen, just skip it.
|
||||
if _, ok := snap.Recents[epochKey]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
checkpointHeader := FindAncientHeader(header, uint64(len(snap.Validators)/2), chain, parents)
|
||||
if checkpointHeader == nil {
|
||||
return nil, consensus.ErrUnknownAncestor
|
||||
@@ -289,15 +301,22 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
}
|
||||
}
|
||||
}
|
||||
oldLimit := len(snap.Validators)/2 + 1
|
||||
newLimit := len(newVals)/2 + 1
|
||||
if newLimit < oldLimit {
|
||||
for i := 0; i < oldLimit-newLimit; i++ {
|
||||
delete(snap.Recents, number-uint64(newLimit)-uint64(i))
|
||||
if chainConfig.IsBohr(header.Number, header.Time) {
|
||||
// BEP-404: Clear Miner History when Switching Validators Set
|
||||
snap.Recents = make(map[uint64]common.Address)
|
||||
snap.Recents[epochKey] = common.Address{}
|
||||
log.Debug("Recents are cleared up", "blockNumber", number)
|
||||
} else {
|
||||
oldLimit := len(snap.Validators)/2 + 1
|
||||
newLimit := len(newVals)/2 + 1
|
||||
if newLimit < oldLimit {
|
||||
for i := 0; i < oldLimit-newLimit; i++ {
|
||||
delete(snap.Recents, number-uint64(newLimit)-uint64(i))
|
||||
}
|
||||
}
|
||||
}
|
||||
oldLimit = len(snap.Validators)
|
||||
newLimit = len(newVals)
|
||||
oldLimit := len(snap.Validators)
|
||||
newLimit := len(newVals)
|
||||
if newLimit < oldLimit {
|
||||
for i := 0; i < oldLimit-newLimit; i++ {
|
||||
delete(snap.RecentForkHashes, number-uint64(newLimit)-uint64(i))
|
||||
@@ -311,10 +330,6 @@ func (s *Snapshot) apply(headers []*types.Header, chain consensus.ChainHeaderRea
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
snap.updateAttestation(header, chainConfig, s.config)
|
||||
|
||||
snap.RecentForkHashes[number] = hex.EncodeToString(header.Extra[extraVanity-nextForkHashSize : extraVanity])
|
||||
}
|
||||
snap.Number += uint64(len(headers))
|
||||
snap.Hash = headers[len(headers)-1].Hash()
|
||||
@@ -338,6 +353,13 @@ func (s *Snapshot) inturn(validator common.Address) bool {
|
||||
return validators[offset] == validator
|
||||
}
|
||||
|
||||
// inturnValidator returns the validator at a given block height.
|
||||
func (s *Snapshot) inturnValidator() common.Address {
|
||||
validators := s.validators()
|
||||
offset := (s.Number + 1) % uint64(len(validators))
|
||||
return validators[offset]
|
||||
}
|
||||
|
||||
func (s *Snapshot) enoughDistance(validator common.Address, header *types.Header) bool {
|
||||
idx := s.indexOfVal(validator)
|
||||
if idx < 0 {
|
||||
|
||||
@@ -189,7 +189,7 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||
// generator function.
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
|
||||
Alloc: types.GenesisAlloc{benchRootAddr: {Balance: benchRootFunds}},
|
||||
}
|
||||
_, chain, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), b.N, gen)
|
||||
|
||||
|
||||
@@ -66,6 +66,31 @@ func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, engin
|
||||
return validator
|
||||
}
|
||||
|
||||
// ValidateListsInBody validates that UncleHash, WithdrawalsHash, and WithdrawalsHash correspond to the lists in the block body, respectively.
|
||||
func ValidateListsInBody(block *types.Block) error {
|
||||
header := block.Header()
|
||||
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
|
||||
return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash)
|
||||
}
|
||||
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
|
||||
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
|
||||
}
|
||||
// Withdrawals are present after the Shanghai fork.
|
||||
if header.WithdrawalsHash != nil {
|
||||
// Withdrawals list must be present in body after Shanghai.
|
||||
if block.Withdrawals() == nil {
|
||||
return errors.New("missing withdrawals in block body")
|
||||
}
|
||||
if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash {
|
||||
return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash)
|
||||
}
|
||||
} else if block.Withdrawals() != nil { // Withdrawals turn into empty from nil when BlockBody has Sidecars
|
||||
// Withdrawals are not allowed prior to shanghai fork
|
||||
return errors.New("withdrawals present in block body")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateBody validates the given block's uncles and verifies the block
|
||||
// header's transaction and uncle roots. The headers are assumed to be already
|
||||
// validated at this point.
|
||||
@@ -83,31 +108,12 @@ func (v *BlockValidator) ValidateBody(block *types.Block) error {
|
||||
if err := v.engine.VerifyUncles(v.bc, block); err != nil {
|
||||
return err
|
||||
}
|
||||
if hash := types.CalcUncleHash(block.Uncles()); hash != header.UncleHash {
|
||||
return fmt.Errorf("uncle root hash mismatch (header value %x, calculated %x)", header.UncleHash, hash)
|
||||
}
|
||||
|
||||
validateFuns := []func() error{
|
||||
func() error {
|
||||
if hash := types.DeriveSha(block.Transactions(), trie.NewStackTrie(nil)); hash != header.TxHash {
|
||||
return fmt.Errorf("transaction root hash mismatch: have %x, want %x", hash, header.TxHash)
|
||||
}
|
||||
return nil
|
||||
return ValidateListsInBody(block)
|
||||
},
|
||||
func() error {
|
||||
// Withdrawals are present after the Shanghai fork.
|
||||
if header.WithdrawalsHash != nil {
|
||||
// Withdrawals list must be present in body after Shanghai.
|
||||
if block.Withdrawals() == nil {
|
||||
return errors.New("missing withdrawals in block body")
|
||||
}
|
||||
if hash := types.DeriveSha(block.Withdrawals(), trie.NewStackTrie(nil)); hash != *header.WithdrawalsHash {
|
||||
return fmt.Errorf("withdrawals root hash mismatch (header value %x, calculated %x)", *header.WithdrawalsHash, hash)
|
||||
}
|
||||
} else if block.Withdrawals() != nil {
|
||||
// Withdrawals are not allowed prior to shanghai fork
|
||||
return errors.New("withdrawals present in block body")
|
||||
}
|
||||
// Blob transactions may be present after the Cancun fork.
|
||||
var blobs int
|
||||
for i, tx := range block.Transactions() {
|
||||
|
||||
@@ -106,7 +106,7 @@ func testHeaderVerificationForMerging(t *testing.T, isClique bool) {
|
||||
gspec = &Genesis{
|
||||
Config: &config,
|
||||
ExtraData: make([]byte, 32+common.AddressLength+crypto.SignatureLength),
|
||||
Alloc: map[common.Address]GenesisAccount{
|
||||
Alloc: map[common.Address]types.Account{
|
||||
addr: {Balance: big.NewInt(1)},
|
||||
},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,18 +48,23 @@ func (st *insertStats) report(chain []*types.Block, index int, snapDiffItems, sn
|
||||
// If we're at the last block of the batch or report period reached, log
|
||||
if index == len(chain)-1 || elapsed >= statsReportLimit {
|
||||
// Count the number of transactions in this segment
|
||||
var txs int
|
||||
var txs, blobs int
|
||||
for _, block := range chain[st.lastIndex : index+1] {
|
||||
txs += len(block.Transactions())
|
||||
for _, sidecar := range block.Sidecars() {
|
||||
blobs += len(sidecar.Blobs)
|
||||
}
|
||||
}
|
||||
end := chain[index]
|
||||
|
||||
// Assemble the log context and send it to the logger
|
||||
mgasps := float64(st.usedGas) * 1000 / float64(elapsed)
|
||||
context := []interface{}{
|
||||
"number", end.Number(), "hash", end.Hash(), "miner", end.Coinbase(),
|
||||
"blocks", st.processed, "txs", txs, "mgas", float64(st.usedGas) / 1000000,
|
||||
"elapsed", common.PrettyDuration(elapsed), "mgasps", float64(st.usedGas) * 1000 / float64(elapsed),
|
||||
"blocks", st.processed, "txs", txs, "blobs", blobs, "mgas", float64(st.usedGas) / 1000000,
|
||||
"elapsed", common.PrettyDuration(elapsed), "mgasps", mgasps,
|
||||
}
|
||||
blockInsertMgaspsGauge.Update(int64(mgasps))
|
||||
if timestamp := time.Unix(int64(end.Time()), 0); time.Since(timestamp) > time.Minute {
|
||||
context = append(context, []interface{}{"age", common.PrettyAge(timestamp)}...)
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
// CurrentHeader retrieves the current head header of the canonical chain. The
|
||||
@@ -98,6 +98,15 @@ func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header {
|
||||
return bc.hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
// GetVerifiedBlockByHash retrieves the header of a verified block, it may be only in memory.
|
||||
func (bc *BlockChain) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
highestVerifiedBlock := bc.highestVerifiedBlock.Load()
|
||||
if highestVerifiedBlock != nil && highestVerifiedBlock.Hash() == hash {
|
||||
return highestVerifiedBlock
|
||||
}
|
||||
return bc.hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
// GetHeaderByNumber retrieves a block header from the database by number,
|
||||
// caching it (associated with its hash) if found.
|
||||
func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||
@@ -247,6 +256,23 @@ func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
|
||||
return receipts
|
||||
}
|
||||
|
||||
// GetSidecarsByHash retrieves the sidecars for all transactions in a given block.
|
||||
func (bc *BlockChain) GetSidecarsByHash(hash common.Hash) types.BlobSidecars {
|
||||
if sidecars, ok := bc.sidecarsCache.Get(hash); ok {
|
||||
return sidecars
|
||||
}
|
||||
number := rawdb.ReadHeaderNumber(bc.db, hash)
|
||||
if number == nil {
|
||||
return nil
|
||||
}
|
||||
sidecars := rawdb.ReadBlobSidecars(bc.db, hash, *number)
|
||||
if sidecars == nil {
|
||||
return nil
|
||||
}
|
||||
bc.sidecarsCache.Add(hash, sidecars)
|
||||
return sidecars
|
||||
}
|
||||
|
||||
// GetUnclesInChain retrieves all the uncles from a given block backwards until
|
||||
// a specific distance is reached.
|
||||
func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
|
||||
@@ -379,7 +405,20 @@ func (bc *BlockChain) State() (*state.StateDB, error) {
|
||||
|
||||
// StateAt returns a new mutable state based on a particular point in time.
|
||||
func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
|
||||
return state.New(root, bc.stateCache, bc.snaps)
|
||||
stateDb, err := state.New(root, bc.stateCache, bc.snaps)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If there's no trie and the specified snapshot is not available, getting
|
||||
// any state will by default return nil.
|
||||
// Instead of that, it will be more useful to return an error to indicate
|
||||
// the state is not available.
|
||||
if stateDb.NoTrie() && stateDb.GetSnap() == nil {
|
||||
return nil, errors.New("state is not available")
|
||||
}
|
||||
|
||||
return stateDb, err
|
||||
}
|
||||
|
||||
// Config retrieves the chain's fork configuration.
|
||||
@@ -432,10 +471,15 @@ func (bc *BlockChain) TxIndexProgress() (TxIndexProgress, error) {
|
||||
}
|
||||
|
||||
// TrieDB retrieves the low level trie database used for data storage.
|
||||
func (bc *BlockChain) TrieDB() *trie.Database {
|
||||
func (bc *BlockChain) TrieDB() *triedb.Database {
|
||||
return bc.triedb
|
||||
}
|
||||
|
||||
// HeaderChain returns the underlying header chain.
|
||||
func (bc *BlockChain) HeaderChain() *HeaderChain {
|
||||
return bc.hc
|
||||
}
|
||||
|
||||
// SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent.
|
||||
func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription {
|
||||
return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch))
|
||||
@@ -451,6 +495,11 @@ func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Su
|
||||
return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// SubscribeHighestVerifiedBlockEvent registers a subscription of HighestVerifiedBlockEvent.
|
||||
func (bc *BlockChain) SubscribeHighestVerifiedHeaderEvent(ch chan<- HighestVerifiedBlockEvent) event.Subscription {
|
||||
return bc.scope.Track(bc.highestVerifiedBlockFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// SubscribeChainBlockEvent registers a subscription of ChainBlockEvent.
|
||||
func (bc *BlockChain) SubscribeChainBlockEvent(ch chan<- ChainHeadEvent) event.Subscription {
|
||||
return bc.scope.Track(bc.chainBlockFeed.Subscribe(ch))
|
||||
@@ -476,3 +525,12 @@ func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscr
|
||||
func (bc *BlockChain) SubscribeFinalizedHeaderEvent(ch chan<- FinalizedHeaderEvent) event.Subscription {
|
||||
return bc.scope.Track(bc.finalizedHeaderFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// AncientTail retrieves the tail the ancients blocks
|
||||
func (bc *BlockChain) AncientTail() (uint64, error) {
|
||||
tail, err := bc.db.BlockStore().Tail()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return tail, nil
|
||||
}
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
@@ -1795,6 +1797,13 @@ func testRepairWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme s
|
||||
config.SnapshotWait = true
|
||||
}
|
||||
config.TriesInMemory = 128
|
||||
|
||||
if err = db.SetupFreezerEnv(ðdb.FreezerEnv{
|
||||
ChainCfg: gspec.Config,
|
||||
BlobExtraReserve: params.DefaultExtraReserveForBlobRequests,
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
chain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
|
||||
@@ -27,6 +27,8 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
@@ -34,9 +36,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/hashdb"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/hashdb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
)
|
||||
|
||||
// rewindTest is a test case for chain rollback upon user request.
|
||||
@@ -1998,6 +2000,13 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||
config.SnapshotWait = true
|
||||
}
|
||||
config.TriesInMemory = 128
|
||||
|
||||
if err = db.SetupFreezerEnv(ðdb.FreezerEnv{
|
||||
ChainCfg: gspec.Config,
|
||||
BlobExtraReserve: params.DefaultExtraReserveForBlobRequests,
|
||||
}); err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
}
|
||||
chain, err := NewBlockChain(db, config, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create chain: %v", err)
|
||||
@@ -2034,13 +2043,13 @@ func testSetHeadWithScheme(t *testing.T, tt *rewindTest, snapshots bool, scheme
|
||||
}
|
||||
// Reopen the trie database without persisting in-memory dirty nodes.
|
||||
chain.triedb.Close()
|
||||
dbconfig := &trie.Config{}
|
||||
dbconfig := &triedb.Config{}
|
||||
if scheme == rawdb.PathScheme {
|
||||
dbconfig.PathDB = pathdb.Defaults
|
||||
} else {
|
||||
dbconfig.HashDB = hashdb.Defaults
|
||||
}
|
||||
chain.triedb = trie.NewDatabase(chain.db, dbconfig)
|
||||
chain.triedb = triedb.NewDatabase(chain.db, dbconfig)
|
||||
chain.stateCache = state.NewDatabaseWithNodeDB(chain.db, chain.triedb)
|
||||
|
||||
// Force run a freeze cycle
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
@@ -26,6 +27,10 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
@@ -922,7 +927,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||
funds = big.NewInt(1000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
@@ -969,7 +974,7 @@ func testFastVsFullChains(t *testing.T, scheme string) {
|
||||
t.Fatalf("failed to insert receipt %d: %v", n, err)
|
||||
}
|
||||
// Freezer style fast import the chain.
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
@@ -1055,7 +1060,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||
funds = big.NewInt(1000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
)
|
||||
@@ -1064,7 +1069,7 @@ func testLightVsFastVsFullChainHeads(t *testing.T, scheme string) {
|
||||
|
||||
// makeDb creates a db instance for testing.
|
||||
makeDb := func() ethdb.Database {
|
||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
@@ -1175,7 +1180,7 @@ func testChainTxReorgs(t *testing.T, scheme string) {
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
GasLimit: 3141592,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
addr1: {Balance: big.NewInt(1000000000000000)},
|
||||
addr2: {Balance: big.NewInt(1000000000000000)},
|
||||
addr3: {Balance: big.NewInt(1000000000000000)},
|
||||
@@ -1290,7 +1295,7 @@ func testLogReorgs(t *testing.T, scheme string) {
|
||||
|
||||
// this code generates a log
|
||||
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
|
||||
@@ -1347,7 +1352,7 @@ func testLogRebirth(t *testing.T, scheme string) {
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
engine = ethash.NewFaker()
|
||||
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
@@ -1429,7 +1434,7 @@ func testSideLogRebirth(t *testing.T, scheme string) {
|
||||
var (
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
blockchain, _ = NewBlockChain(rawdb.NewMemoryDatabase(), DefaultCacheConfigWithScheme(scheme), gspec, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
)
|
||||
@@ -1526,7 +1531,7 @@ func testReorgSideEvent(t *testing.T, scheme string) {
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
|
||||
Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}},
|
||||
}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
)
|
||||
@@ -1669,7 +1674,7 @@ func testEIP155Transition(t *testing.T, scheme string) {
|
||||
EIP155Block: big.NewInt(2),
|
||||
HomesteadBlock: new(big.Int),
|
||||
},
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
|
||||
Alloc: types.GenesisAlloc{address: {Balance: funds}, deleteAddr: {Balance: new(big.Int)}},
|
||||
}
|
||||
)
|
||||
genDb, blocks, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 4, func(i int, block *BlockGen) {
|
||||
@@ -1784,7 +1789,7 @@ func testEIP161AccountRemoval(t *testing.T, scheme string) {
|
||||
EIP150Block: new(big.Int),
|
||||
EIP158Block: big.NewInt(2),
|
||||
},
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||
}
|
||||
)
|
||||
_, blocks, _ := GenerateChainWithGenesis(gspec, ethash.NewFaker(), 3, func(i int, block *BlockGen) {
|
||||
@@ -1952,7 +1957,7 @@ func testLargeReorgTrieGC(t *testing.T, scheme string) {
|
||||
competitor, _ := GenerateChain(genesis.Config, shared[len(shared)-1], engine, genDb, 2*TriesInMemory+1, func(i int, b *BlockGen) { b.SetCoinbase(common.Address{3}) })
|
||||
|
||||
// Import the shared chain and the original canonical one
|
||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
||||
db, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||
defer db.Close()
|
||||
|
||||
chain, err := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
@@ -2015,13 +2020,13 @@ func testBlockchainRecovery(t *testing.T, scheme string) {
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{address: {Balance: funds}}}
|
||||
gspec = &Genesis{Config: params.TestChainConfig, Alloc: types.GenesisAlloc{address: {Balance: funds}}}
|
||||
)
|
||||
height := uint64(1024)
|
||||
_, blocks, receipts := GenerateChainWithGenesis(gspec, ethash.NewFaker(), int(height), nil)
|
||||
|
||||
// Import the chain as a ancient-first node and ensure all pointers are updated
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
@@ -2092,7 +2097,7 @@ func testInsertReceiptChainRollback(t *testing.T, scheme string) {
|
||||
}
|
||||
|
||||
// Set up a BlockChain that uses the ancient store.
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
||||
ancientDb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
@@ -2162,7 +2167,7 @@ func testLowDiffLongChain(t *testing.T, scheme string) {
|
||||
})
|
||||
|
||||
// Import the canonical chain
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||
defer diskdb.Close()
|
||||
|
||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), genesis, nil, engine, vm.Config{}, nil, nil)
|
||||
@@ -2220,7 +2225,7 @@ func testSideImport(t *testing.T, numCanonBlocksInSidechain, blocksBetweenCommon
|
||||
|
||||
gspec = &Genesis{
|
||||
Config: &chainConfig,
|
||||
Alloc: GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
|
||||
Alloc: types.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
@@ -2379,7 +2384,7 @@ func testInsertKnownChainData(t *testing.T, typ string, scheme string) {
|
||||
b.OffsetTime(-9) // A higher difficulty
|
||||
})
|
||||
// Import the shared chain and the original canonical one
|
||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
@@ -2550,7 +2555,7 @@ func testInsertKnownChainDataWithMerging(t *testing.T, typ string, mergeHeight i
|
||||
}
|
||||
})
|
||||
// Import the shared chain and the original canonical one
|
||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
||||
chaindb, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create temp freezer db: %v", err)
|
||||
}
|
||||
@@ -2815,7 +2820,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
|
||||
bankFunds = big.NewInt(100000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
testBankAddress: {Balance: bankFunds},
|
||||
common.HexToAddress("0xc0de"): {
|
||||
Code: []byte{0x60, 0x01, 0x50},
|
||||
@@ -2993,7 +2998,7 @@ func testDeleteCreateRevert(t *testing.T, scheme string) {
|
||||
funds = big.NewInt(100000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
// The address 0xAAAAA selfdestructs if called
|
||||
aa: {
|
||||
@@ -3117,7 +3122,7 @@ func testDeleteRecreateSlots(t *testing.T, scheme string) {
|
||||
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
// The address 0xAAAAA selfdestructs if called
|
||||
aa: {
|
||||
@@ -3203,7 +3208,7 @@ func testDeleteRecreateAccount(t *testing.T, scheme string) {
|
||||
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
// The address 0xAAAAA selfdestructs if called
|
||||
aa: {
|
||||
@@ -3324,7 +3329,7 @@ func testDeleteRecreateSlotsAcrossManyBlocks(t *testing.T, scheme string) {
|
||||
t.Logf("Destination address: %x\n", aa)
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
// The address 0xAAAAA selfdestructs if called
|
||||
aa: {
|
||||
@@ -3519,7 +3524,7 @@ func testInitThenFailCreateContract(t *testing.T, scheme string) {
|
||||
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
// The address aa has some funds
|
||||
aa: {Balance: big.NewInt(100000)},
|
||||
@@ -3585,10 +3590,20 @@ func TestEIP2718TransitionWithTestChainConfig(t *testing.T) {
|
||||
testEIP2718TransitionWithConfig(t, rawdb.HashScheme, params.TestChainConfig)
|
||||
}
|
||||
|
||||
func preShanghaiConfig() *params.ChainConfig {
|
||||
config := *params.ParliaTestChainConfig
|
||||
config.ShanghaiTime = nil
|
||||
config.KeplerTime = nil
|
||||
config.FeynmanTime = nil
|
||||
config.FeynmanFixTime = nil
|
||||
config.CancunTime = nil
|
||||
return &config
|
||||
}
|
||||
|
||||
// TestEIP2718TransitionWithParliaConfig tests EIP-2718 with Parlia Config.
|
||||
func TestEIP2718TransitionWithParliaConfig(t *testing.T) {
|
||||
testEIP2718TransitionWithConfig(t, rawdb.HashScheme, params.ParliaTestChainConfig)
|
||||
testEIP2718TransitionWithConfig(t, rawdb.PathScheme, params.ParliaTestChainConfig)
|
||||
testEIP2718TransitionWithConfig(t, rawdb.HashScheme, preShanghaiConfig())
|
||||
testEIP2718TransitionWithConfig(t, rawdb.PathScheme, preShanghaiConfig())
|
||||
}
|
||||
|
||||
// testEIP2718TransitionWithConfig tests EIP02718 with given ChainConfig.
|
||||
@@ -3603,7 +3618,7 @@ func testEIP2718TransitionWithConfig(t *testing.T, scheme string, config *params
|
||||
funds = big.NewInt(1000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: config,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
// The address 0xAAAA sloads 0x00 and 0x01
|
||||
aa: {
|
||||
@@ -3688,7 +3703,7 @@ func testEIP1559Transition(t *testing.T, scheme string) {
|
||||
config = *params.AllEthashProtocolChanges
|
||||
gspec = &Genesis{
|
||||
Config: &config,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
addr1: {Balance: funds},
|
||||
addr2: {Balance: funds},
|
||||
// The address 0xAAAA sloads 0x00 and 0x01
|
||||
@@ -3829,7 +3844,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
||||
funds = big.NewInt(100000000000000000)
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{address: {Balance: funds}},
|
||||
Alloc: types.GenesisAlloc{address: {Balance: funds}},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
signer = types.LatestSigner(gspec.Config)
|
||||
@@ -3843,7 +3858,7 @@ func testSetCanonical(t *testing.T, scheme string) {
|
||||
}
|
||||
gen.AddTx(tx)
|
||||
})
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false)
|
||||
diskdb, _ := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), t.TempDir(), "", false, false, false, false, false)
|
||||
defer diskdb.Close()
|
||||
|
||||
chain, err := NewBlockChain(diskdb, DefaultCacheConfigWithScheme(scheme), gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
@@ -3946,7 +3961,7 @@ func testCanonicalHashMarker(t *testing.T, scheme string) {
|
||||
var (
|
||||
gspec = &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{},
|
||||
Alloc: types.GenesisAlloc{},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
}
|
||||
engine = ethash.NewFaker()
|
||||
@@ -4059,7 +4074,7 @@ func testCreateThenDelete(t *testing.T, config *params.ChainConfig) {
|
||||
}...)
|
||||
gspec := &Genesis{
|
||||
Config: config,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
},
|
||||
}
|
||||
@@ -4145,7 +4160,7 @@ func TestDeleteThenCreate(t *testing.T) {
|
||||
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
},
|
||||
}
|
||||
@@ -4257,7 +4272,7 @@ func TestTransientStorageReset(t *testing.T) {
|
||||
}...)
|
||||
gspec := &Genesis{
|
||||
Config: params.TestChainConfig,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
},
|
||||
}
|
||||
@@ -4325,7 +4340,7 @@ func TestEIP3651(t *testing.T) {
|
||||
config = *params.AllEthashProtocolChanges
|
||||
gspec = &Genesis{
|
||||
Config: &config,
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
addr1: {Balance: funds},
|
||||
addr2: {Balance: funds},
|
||||
// The address 0xAAAA sloads 0x00 and 0x01
|
||||
@@ -4418,3 +4433,134 @@ func TestEIP3651(t *testing.T) {
|
||||
t.Fatalf("sender balance incorrect: expected %d, got %d", expected, actual)
|
||||
}
|
||||
}
|
||||
|
||||
type mockParlia struct {
|
||||
consensus.Engine
|
||||
}
|
||||
|
||||
func (c *mockParlia) Author(header *types.Header) (common.Address, error) {
|
||||
return header.Coinbase, nil
|
||||
}
|
||||
|
||||
func (c *mockParlia) VerifyUncles(chain consensus.ChainReader, block *types.Block) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *mockParlia) VerifyHeader(chain consensus.ChainHeaderReader, header *types.Header) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *mockParlia) VerifyHeaders(chain consensus.ChainHeaderReader, headers []*types.Header) (chan<- struct{}, <-chan error) {
|
||||
abort := make(chan<- struct{})
|
||||
results := make(chan error, len(headers))
|
||||
for i := 0; i < len(headers); i++ {
|
||||
results <- nil
|
||||
}
|
||||
return abort, results
|
||||
}
|
||||
|
||||
func (c *mockParlia) Finalize(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, _ *[]*types.Transaction, uncles []*types.Header, withdrawals []*types.Withdrawal,
|
||||
_ *[]*types.Receipt, _ *[]*types.Transaction, _ *uint64) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c *mockParlia) FinalizeAndAssemble(chain consensus.ChainHeaderReader, header *types.Header, state *state.StateDB, txs []*types.Transaction,
|
||||
uncles []*types.Header, receipts []*types.Receipt, withdrawals []*types.Withdrawal) (*types.Block, []*types.Receipt, error) {
|
||||
// Finalize block
|
||||
c.Finalize(chain, header, state, &txs, uncles, nil, nil, nil, nil)
|
||||
|
||||
// Assign the final state root to header.
|
||||
header.Root = state.IntermediateRoot(chain.Config().IsEIP158(header.Number))
|
||||
|
||||
// Header seems complete, assemble into a block and return
|
||||
return types.NewBlock(header, txs, uncles, receipts, trie.NewStackTrie(nil)), receipts, nil
|
||||
}
|
||||
|
||||
func (c *mockParlia) CalcDifficulty(chain consensus.ChainHeaderReader, time uint64, parent *types.Header) *big.Int {
|
||||
return big.NewInt(1)
|
||||
}
|
||||
|
||||
func TestParliaBlobFeeReward(t *testing.T) {
|
||||
// Have N headers in the freezer
|
||||
frdir := t.TempDir()
|
||||
db, err := rawdb.NewDatabaseWithFreezer(rawdb.NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create database with ancient backend")
|
||||
}
|
||||
config := params.ParliaTestChainConfig
|
||||
gspec := &Genesis{
|
||||
Config: config,
|
||||
Alloc: types.GenesisAlloc{testAddr: {Balance: new(big.Int).SetUint64(10 * params.Ether)}},
|
||||
}
|
||||
engine := &mockParlia{}
|
||||
chain, _ := NewBlockChain(db, nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
signer := types.LatestSigner(config)
|
||||
|
||||
_, bs, _ := GenerateChainWithGenesis(gspec, engine, 1, func(i int, gen *BlockGen) {
|
||||
tx, _ := makeMockTx(config, signer, testKey, gen.TxNonce(testAddr), gen.BaseFee().Uint64(), eip4844.CalcBlobFee(gen.ExcessBlobGas()).Uint64(), false)
|
||||
gen.AddTxWithChain(chain, tx)
|
||||
tx, sidecar := makeMockTx(config, signer, testKey, gen.TxNonce(testAddr), gen.BaseFee().Uint64(), eip4844.CalcBlobFee(gen.ExcessBlobGas()).Uint64(), true)
|
||||
gen.AddTxWithChain(chain, tx)
|
||||
gen.AddBlobSidecar(&types.BlobSidecar{
|
||||
BlobTxSidecar: *sidecar,
|
||||
TxIndex: 1,
|
||||
TxHash: tx.Hash(),
|
||||
})
|
||||
})
|
||||
if _, err := chain.InsertChain(bs); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stateDB, err := chain.State()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
expect := new(big.Int)
|
||||
for _, block := range bs {
|
||||
receipts := chain.GetReceiptsByHash(block.Hash())
|
||||
for _, receipt := range receipts {
|
||||
if receipt.BlobGasPrice != nil {
|
||||
blob := receipt.BlobGasPrice.Mul(receipt.BlobGasPrice, new(big.Int).SetUint64(receipt.BlobGasUsed))
|
||||
expect.Add(expect, blob)
|
||||
}
|
||||
plain := receipt.EffectiveGasPrice.Mul(receipt.EffectiveGasPrice, new(big.Int).SetUint64(receipt.GasUsed))
|
||||
expect.Add(expect, plain)
|
||||
}
|
||||
}
|
||||
actual := stateDB.GetBalance(params.SystemAddress)
|
||||
require.Equal(t, expect.Uint64(), actual.Uint64())
|
||||
}
|
||||
|
||||
func makeMockTx(config *params.ChainConfig, signer types.Signer, key *ecdsa.PrivateKey, nonce uint64, baseFee uint64, blobBaseFee uint64, isBlobTx bool) (*types.Transaction, *types.BlobTxSidecar) {
|
||||
if !isBlobTx {
|
||||
raw := &types.DynamicFeeTx{
|
||||
ChainID: config.ChainID,
|
||||
Nonce: nonce,
|
||||
GasTipCap: big.NewInt(10),
|
||||
GasFeeCap: new(big.Int).SetUint64(baseFee + 10),
|
||||
Gas: params.TxGas,
|
||||
To: &common.Address{0x00},
|
||||
Value: big.NewInt(0),
|
||||
}
|
||||
tx, _ := types.SignTx(types.NewTx(raw), signer, key)
|
||||
return tx, nil
|
||||
}
|
||||
sidecar := &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
||||
}
|
||||
raw := &types.BlobTx{
|
||||
ChainID: uint256.MustFromBig(config.ChainID),
|
||||
Nonce: nonce,
|
||||
GasTipCap: uint256.NewInt(10),
|
||||
GasFeeCap: uint256.NewInt(baseFee + 10),
|
||||
Gas: params.TxGas,
|
||||
To: common.Address{0x00},
|
||||
Value: uint256.NewInt(0),
|
||||
BlobFeeCap: uint256.NewInt(blobBaseFee),
|
||||
BlobHashes: sidecar.BlobHashes(),
|
||||
}
|
||||
tx, _ := types.SignTx(types.NewTx(raw), signer, key)
|
||||
return tx, sidecar
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
@@ -52,6 +52,9 @@ type BlockGen struct {
|
||||
withdrawals []*types.Withdrawal
|
||||
|
||||
engine consensus.Engine
|
||||
|
||||
// extra data of block
|
||||
sidecars types.BlobSidecars
|
||||
}
|
||||
|
||||
// SetCoinbase sets the coinbase of the generated block.
|
||||
@@ -84,7 +87,7 @@ func (b *BlockGen) SetDifficulty(diff *big.Int) {
|
||||
b.header.Difficulty = diff
|
||||
}
|
||||
|
||||
// SetPos makes the header a PoS-header (0 difficulty)
|
||||
// SetPoS makes the header a PoS-header (0 difficulty)
|
||||
func (b *BlockGen) SetPoS() {
|
||||
b.header.Difficulty = new(big.Int)
|
||||
}
|
||||
@@ -171,6 +174,11 @@ func (b *BlockGen) AddUncheckedTx(tx *types.Transaction) {
|
||||
b.txs = append(b.txs, tx)
|
||||
}
|
||||
|
||||
// AddBlobSidecar add block's blob sidecar for DA checking.
|
||||
func (b *BlockGen) AddBlobSidecar(sidecar *types.BlobSidecar) {
|
||||
b.sidecars = append(b.sidecars, sidecar)
|
||||
}
|
||||
|
||||
// Number returns the block number of the block being generated.
|
||||
func (b *BlockGen) Number() *big.Int {
|
||||
return new(big.Int).Set(b.header.Number)
|
||||
@@ -186,6 +194,15 @@ func (b *BlockGen) BaseFee() *big.Int {
|
||||
return new(big.Int).Set(b.header.BaseFee)
|
||||
}
|
||||
|
||||
// ExcessBlobGas returns the EIP-4844 ExcessBlobGas of the block.
|
||||
func (b *BlockGen) ExcessBlobGas() uint64 {
|
||||
excessBlobGas := b.header.ExcessBlobGas
|
||||
if excessBlobGas == nil {
|
||||
return 0
|
||||
}
|
||||
return *excessBlobGas
|
||||
}
|
||||
|
||||
// Gas returns the amount of gas left in the current block.
|
||||
func (b *BlockGen) Gas() uint64 {
|
||||
return b.header.GasLimit - b.header.GasUsed
|
||||
@@ -313,7 +330,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||
}
|
||||
cm := newChainMaker(parent, config, engine)
|
||||
|
||||
genblock := func(i int, parent *types.Block, triedb *trie.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||
genblock := func(i int, parent *types.Block, triedb *triedb.Database, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||
b := &BlockGen{i: i, cm: cm, parent: parent, statedb: statedb, engine: engine}
|
||||
b.header = cm.makeHeader(parent, statedb, b.engine)
|
||||
|
||||
@@ -355,6 +372,16 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if block.Header().EmptyWithdrawalsHash() {
|
||||
block = block.WithWithdrawals(make([]*types.Withdrawal, 0))
|
||||
}
|
||||
if config.IsCancun(block.Number(), block.Time()) {
|
||||
for _, s := range b.sidecars {
|
||||
s.BlockNumber = block.Number()
|
||||
s.BlockHash = block.Hash()
|
||||
}
|
||||
block = block.WithSidecars(b.sidecars)
|
||||
}
|
||||
|
||||
// Write state changes to db
|
||||
root, _, err := statedb.Commit(b.header.Number.Uint64(), nil)
|
||||
@@ -370,7 +397,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||
}
|
||||
|
||||
// Forcibly use hash-based state scheme for retaining all nodes in disk.
|
||||
triedb := trie.NewDatabase(db, trie.HashDefaults)
|
||||
triedb := triedb.NewDatabase(db, triedb.HashDefaults)
|
||||
defer triedb.Close()
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
@@ -415,7 +442,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||
// then generate chain on top.
|
||||
func GenerateChainWithGenesis(genesis *Genesis, engine consensus.Engine, n int, gen func(int, *BlockGen)) (ethdb.Database, []*types.Block, []types.Receipts) {
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
triedb := trie.NewDatabase(db, trie.HashDefaults)
|
||||
triedb := triedb.NewDatabase(db, triedb.HashDefaults)
|
||||
defer triedb.Close()
|
||||
_, err := genesis.Commit(db, triedb)
|
||||
if err != nil {
|
||||
@@ -456,7 +483,12 @@ func (cm *chainMaker) makeHeader(parent *types.Block, state *state.StateDB, engi
|
||||
excessBlobGas := eip4844.CalcExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed)
|
||||
header.ExcessBlobGas = &excessBlobGas
|
||||
header.BlobGasUsed = new(uint64)
|
||||
header.ParentBeaconRoot = new(common.Hash)
|
||||
if cm.config.Parlia != nil {
|
||||
header.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||
}
|
||||
if cm.config.Parlia == nil || cm.config.IsBohr(header.Number, header.Time) {
|
||||
header.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
}
|
||||
return header
|
||||
}
|
||||
@@ -588,3 +620,11 @@ func (cm *chainMaker) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||
func (cm *chainMaker) GetHighestVerifiedHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (cm *chainMaker) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
return cm.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
func (cm *chainMaker) ChasingHead() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
func TestGeneratePOSChain(t *testing.T) {
|
||||
@@ -46,9 +46,9 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||
asm4788 = common.Hex2Bytes("3373fffffffffffffffffffffffffffffffffffffffe14604d57602036146024575f5ffd5b5f35801560495762001fff810690815414603c575f5ffd5b62001fff01545f5260205ff35b5f5ffd5b62001fff42064281555f359062001fff015500")
|
||||
gspec = &Genesis{
|
||||
Config: &config,
|
||||
Alloc: GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
params.BeaconRootsStorageAddress: {Balance: common.Big0, Code: asm4788},
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: funds},
|
||||
params.BeaconRootsAddress: {Balance: common.Big0, Code: asm4788},
|
||||
},
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Difficulty: common.Big1,
|
||||
@@ -69,19 +69,19 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||
storage[common.Hash{0x01}] = common.Hash{0x01}
|
||||
storage[common.Hash{0x02}] = common.Hash{0x02}
|
||||
storage[common.Hash{0x03}] = common.HexToHash("0303")
|
||||
gspec.Alloc[aa] = GenesisAccount{
|
||||
gspec.Alloc[aa] = types.Account{
|
||||
Balance: common.Big1,
|
||||
Nonce: 1,
|
||||
Storage: storage,
|
||||
Code: common.Hex2Bytes("6042"),
|
||||
}
|
||||
gspec.Alloc[bb] = GenesisAccount{
|
||||
gspec.Alloc[bb] = types.Account{
|
||||
Balance: common.Big2,
|
||||
Nonce: 1,
|
||||
Storage: storage,
|
||||
Code: common.Hex2Bytes("600154600354"),
|
||||
}
|
||||
genesis := gspec.MustCommit(gendb, trie.NewDatabase(gendb, trie.HashDefaults))
|
||||
genesis := gspec.MustCommit(gendb, triedb.NewDatabase(gendb, triedb.HashDefaults))
|
||||
|
||||
genchain, genreceipts := GenerateChain(gspec.Config, genesis, beacon.NewFaker(), gendb, 4, func(i int, gen *BlockGen) {
|
||||
gen.SetParentBeaconRoot(common.Hash{byte(i + 1)})
|
||||
@@ -180,7 +180,7 @@ func TestGeneratePOSChain(t *testing.T) {
|
||||
}
|
||||
state, _ := blockchain.State()
|
||||
idx := block.Time()%8191 + 8191
|
||||
got := state.GetState(params.BeaconRootsStorageAddress, common.BigToHash(new(big.Int).SetUint64(idx)))
|
||||
got := state.GetState(params.BeaconRootsAddress, common.BigToHash(new(big.Int).SetUint64(idx)))
|
||||
if got != want {
|
||||
t.Fatalf("block %d, wrong parent beacon root in state: got %s, want %s", i, got, want)
|
||||
}
|
||||
@@ -202,9 +202,9 @@ func ExampleGenerateChain() {
|
||||
// Ensure that key1 has some funds in the genesis block.
|
||||
gspec := &Genesis{
|
||||
Config: ¶ms.ChainConfig{HomesteadBlock: new(big.Int)},
|
||||
Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
|
||||
Alloc: types.GenesisAlloc{addr1: {Balance: big.NewInt(1000000)}},
|
||||
}
|
||||
genesis := gspec.MustCommit(genDb, trie.NewDatabase(genDb, trie.HashDefaults))
|
||||
genesis := gspec.MustCommit(genDb, triedb.NewDatabase(genDb, triedb.HashDefaults))
|
||||
|
||||
// This call generates a chain of 5 blocks. The function runs for
|
||||
// each block and adds different features to gen based on the
|
||||
|
||||
162
core/data_availability.go
Normal file
162
core/data_availability.go
Normal file
@@ -0,0 +1,162 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/gopool"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
daCheckTimer = metrics.NewRegisteredTimer("chain/dacheck", nil)
|
||||
)
|
||||
|
||||
// validateBlobSidecar it is same as validateBlobSidecar in core/txpool/validation.go
|
||||
func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobSidecar) error {
|
||||
if len(sidecar.Blobs) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))
|
||||
}
|
||||
if len(sidecar.Commitments) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blob commitments compared to %d blob hashes", len(sidecar.Commitments), len(hashes))
|
||||
}
|
||||
if len(sidecar.Proofs) != len(hashes) {
|
||||
return fmt.Errorf("invalid number of %d blob proofs compared to %d blob hashes", len(sidecar.Proofs), len(hashes))
|
||||
}
|
||||
// Blob quantities match up, validate that the provers match with the
|
||||
// transaction hash before getting to the cryptography
|
||||
hasher := sha256.New()
|
||||
for i, vhash := range hashes {
|
||||
computed := kzg4844.CalcBlobHashV1(hasher, &sidecar.Commitments[i])
|
||||
if vhash != computed {
|
||||
return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, computed, vhash)
|
||||
}
|
||||
}
|
||||
// Blob commitments match with the hashes in the transaction, verify the
|
||||
// blobs themselves via KZG
|
||||
for i := range sidecar.Blobs {
|
||||
if err := kzg4844.VerifyBlobProof(sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil {
|
||||
return fmt.Errorf("invalid blob %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsDataAvailable it checks that the blobTx block has available blob data
|
||||
func IsDataAvailable(chain consensus.ChainHeaderReader, block *types.Block) (err error) {
|
||||
defer func(start time.Time) {
|
||||
daCheckTimer.Update(time.Since(start))
|
||||
}(time.Now())
|
||||
|
||||
// refer logic in ValidateBody
|
||||
if !chain.Config().IsCancun(block.Number(), block.Time()) {
|
||||
if block.Sidecars() != nil {
|
||||
return errors.New("sidecars present in block body before cancun")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// only required to check within MinBlocksForBlobRequests block's DA
|
||||
highest := chain.ChasingHead()
|
||||
current := chain.CurrentHeader()
|
||||
if highest == nil || highest.Number.Cmp(current.Number) < 0 {
|
||||
highest = current
|
||||
}
|
||||
if block.NumberU64()+params.MinBlocksForBlobRequests < highest.Number.Uint64() {
|
||||
// if we needn't check DA of this block, just clean it
|
||||
block.CleanSidecars()
|
||||
return nil
|
||||
}
|
||||
|
||||
// if sidecar is nil, just clean it. And it will be used for saving in ancient.
|
||||
if block.Sidecars() == nil {
|
||||
block.CleanSidecars()
|
||||
}
|
||||
sidecars := block.Sidecars()
|
||||
for _, s := range sidecars {
|
||||
if err := s.SanityCheck(block.Number(), block.Hash()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// alloc block's blobTx
|
||||
blobTxs := make([]*types.Transaction, 0, len(sidecars))
|
||||
blobTxIndexes := make([]uint64, 0, len(sidecars))
|
||||
for i, tx := range block.Transactions() {
|
||||
if tx.Type() != types.BlobTxType {
|
||||
continue
|
||||
}
|
||||
blobTxs = append(blobTxs, tx)
|
||||
blobTxIndexes = append(blobTxIndexes, uint64(i))
|
||||
}
|
||||
if len(blobTxs) != len(sidecars) {
|
||||
return fmt.Errorf("blob info mismatch: sidecars %d, versionedHashes:%d", len(sidecars), len(blobTxs))
|
||||
}
|
||||
|
||||
// check blob amount
|
||||
blobCnt := 0
|
||||
for _, s := range sidecars {
|
||||
blobCnt += len(s.Blobs)
|
||||
}
|
||||
if blobCnt > params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob {
|
||||
return fmt.Errorf("too many blobs in block: have %d, permitted %d", blobCnt, params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)
|
||||
}
|
||||
|
||||
// check blob and versioned hash
|
||||
for i, tx := range blobTxs {
|
||||
// check sidecar tx related
|
||||
if sidecars[i].TxHash != tx.Hash() {
|
||||
return fmt.Errorf("sidecar's TxHash mismatch with expected transaction, want: %v, have: %v", sidecars[i].TxHash, tx.Hash())
|
||||
}
|
||||
if sidecars[i].TxIndex != blobTxIndexes[i] {
|
||||
return fmt.Errorf("sidecar's TxIndex mismatch with expected transaction, want: %v, have: %v", sidecars[i].TxIndex, blobTxIndexes[i])
|
||||
}
|
||||
if err := validateBlobSidecar(tx.BlobHashes(), sidecars[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func CheckDataAvailableInBatch(chainReader consensus.ChainHeaderReader, chain types.Blocks) (int, error) {
|
||||
if len(chain) == 1 {
|
||||
return 0, IsDataAvailable(chainReader, chain[0])
|
||||
}
|
||||
|
||||
var (
|
||||
wg sync.WaitGroup
|
||||
errs sync.Map
|
||||
)
|
||||
|
||||
for i := range chain {
|
||||
wg.Add(1)
|
||||
func(index int, block *types.Block) {
|
||||
gopool.Submit(func() {
|
||||
defer wg.Done()
|
||||
errs.Store(index, IsDataAvailable(chainReader, block))
|
||||
})
|
||||
}(i, chain[i])
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
for i := range chain {
|
||||
val, exist := errs.Load(i)
|
||||
if !exist || val == nil {
|
||||
continue
|
||||
}
|
||||
err := val.(error)
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
440
core/data_availability_test.go
Normal file
440
core/data_availability_test.go
Normal file
@@ -0,0 +1,440 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/consensys/gnark-crypto/ecc/bls12-381/fr"
|
||||
gokzg4844 "github.com/crate-crypto/go-kzg-4844"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
emptyBlob = kzg4844.Blob{}
|
||||
emptyBlobCommit, _ = kzg4844.BlobToCommitment(emptyBlob)
|
||||
emptyBlobProof, _ = kzg4844.ComputeBlobProof(emptyBlob, emptyBlobCommit)
|
||||
)
|
||||
|
||||
func TestIsDataAvailable(t *testing.T) {
|
||||
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
||||
tests := []struct {
|
||||
block *types.Block
|
||||
chasingHead uint64
|
||||
withSidecar bool
|
||||
err bool
|
||||
}{
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: 1,
|
||||
withSidecar: true,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), nil),
|
||||
}, nil),
|
||||
chasingHead: 1,
|
||||
withSidecar: true,
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: 1,
|
||||
withSidecar: false,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: 1,
|
||||
withSidecar: true,
|
||||
err: false,
|
||||
},
|
||||
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: params.MinBlocksForBlobRequests + 1,
|
||||
withSidecar: true,
|
||||
err: true,
|
||||
},
|
||||
{
|
||||
block: types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(0),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
chasingHead: params.MinBlocksForBlobRequests + 1,
|
||||
withSidecar: false,
|
||||
err: false,
|
||||
},
|
||||
}
|
||||
|
||||
for i, item := range tests {
|
||||
if item.withSidecar {
|
||||
item.block = item.block.WithSidecars(collectBlobsFromTxs(item.block.Header(), item.block.Transactions()))
|
||||
}
|
||||
hr.setChasingHead(item.chasingHead)
|
||||
err := IsDataAvailable(hr, item.block)
|
||||
if item.err {
|
||||
require.Error(t, err, i)
|
||||
t.Log(err)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err, i)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckDataAvailableInBatch(t *testing.T) {
|
||||
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
||||
tests := []struct {
|
||||
chain types.Blocks
|
||||
err bool
|
||||
index int
|
||||
}{
|
||||
{
|
||||
chain: types.Blocks{
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(2),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
},
|
||||
err: false,
|
||||
},
|
||||
{
|
||||
chain: types.Blocks{
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(2),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(3),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
},
|
||||
err: true,
|
||||
index: 1,
|
||||
},
|
||||
{
|
||||
chain: types.Blocks{
|
||||
types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), nil),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
createMockDATx(hr.Config(), &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob, emptyBlob, emptyBlob, emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit, emptyBlobCommit, emptyBlobCommit, emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof, emptyBlobProof, emptyBlobProof, emptyBlobProof},
|
||||
}),
|
||||
}, nil),
|
||||
},
|
||||
err: true,
|
||||
index: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for i, item := range tests {
|
||||
for j, block := range item.chain {
|
||||
item.chain[j] = block.WithSidecars(collectBlobsFromTxs(block.Header(), block.Transactions()))
|
||||
}
|
||||
index, err := CheckDataAvailableInBatch(hr, item.chain)
|
||||
if item.err {
|
||||
t.Log(index, err)
|
||||
require.Error(t, err, i)
|
||||
require.Equal(t, item.index, index, i)
|
||||
continue
|
||||
}
|
||||
require.NoError(t, err, i)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkEmptySidecarDAChecking(b *testing.B) {
|
||||
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
createMockDATx(hr.Config(), emptySidecar()),
|
||||
}, nil)
|
||||
block = block.WithSidecars(collectBlobsFromTxs(block.Header(), block.Transactions()))
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsDataAvailable(hr, block)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRandomSidecarDAChecking(b *testing.B) {
|
||||
hr := NewMockDAHeaderReader(params.ParliaTestChainConfig)
|
||||
const count = 10
|
||||
blocks := make([]*types.Block, count)
|
||||
for i := 0; i < len(blocks); i++ {
|
||||
block := types.NewBlockWithHeader(&types.Header{
|
||||
Number: big.NewInt(1),
|
||||
}).WithBody(types.Transactions{
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
createMockDATx(hr.Config(), randomSidecar()),
|
||||
}, nil)
|
||||
block = block.WithSidecars(collectBlobsFromTxs(block.Header(), block.Transactions()))
|
||||
blocks[i] = block
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
IsDataAvailable(hr, blocks[i%count])
|
||||
}
|
||||
}
|
||||
|
||||
func collectBlobsFromTxs(header *types.Header, txs types.Transactions) types.BlobSidecars {
|
||||
sidecars := make(types.BlobSidecars, 0, len(txs))
|
||||
for i, tx := range txs {
|
||||
sidecar := types.NewBlobSidecarFromTx(tx)
|
||||
if sidecar == nil {
|
||||
continue
|
||||
}
|
||||
sidecar.TxIndex = uint64(i)
|
||||
sidecar.TxHash = tx.Hash()
|
||||
sidecar.BlockNumber = header.Number
|
||||
sidecar.BlockHash = header.Hash()
|
||||
sidecars = append(sidecars, sidecar)
|
||||
}
|
||||
return sidecars
|
||||
}
|
||||
|
||||
type mockDAHeaderReader struct {
|
||||
config *params.ChainConfig
|
||||
chasingHead uint64
|
||||
}
|
||||
|
||||
func NewMockDAHeaderReader(config *params.ChainConfig) *mockDAHeaderReader {
|
||||
return &mockDAHeaderReader{
|
||||
config: config,
|
||||
chasingHead: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) setChasingHead(h uint64) {
|
||||
r.chasingHead = h
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) Config() *params.ChainConfig {
|
||||
return r.config
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) CurrentHeader() *types.Header {
|
||||
return &types.Header{
|
||||
Number: new(big.Int).SetUint64(r.chasingHead),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) ChasingHead() *types.Header {
|
||||
return &types.Header{
|
||||
Number: new(big.Int).SetUint64(r.chasingHead),
|
||||
}
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GenesisHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetHeader(hash common.Hash, number uint64) *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetHeaderByNumber(number uint64) *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetHeaderByHash(hash common.Hash) *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetTd(hash common.Hash, number uint64) *big.Int {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetHighestVerifiedHeader() *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func (r *mockDAHeaderReader) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
panic("not supported")
|
||||
}
|
||||
|
||||
func createMockDATx(config *params.ChainConfig, sidecar *types.BlobTxSidecar) *types.Transaction {
|
||||
if sidecar == nil {
|
||||
tx := &types.DynamicFeeTx{
|
||||
ChainID: config.ChainID,
|
||||
Nonce: 0,
|
||||
GasTipCap: big.NewInt(22),
|
||||
GasFeeCap: big.NewInt(5),
|
||||
Gas: 25000,
|
||||
To: &common.Address{0x03, 0x04, 0x05},
|
||||
Value: big.NewInt(99),
|
||||
Data: make([]byte, 50),
|
||||
}
|
||||
return types.NewTx(tx)
|
||||
}
|
||||
tx := &types.BlobTx{
|
||||
ChainID: uint256.MustFromBig(config.ChainID),
|
||||
Nonce: 5,
|
||||
GasTipCap: uint256.NewInt(22),
|
||||
GasFeeCap: uint256.NewInt(5),
|
||||
Gas: 25000,
|
||||
To: common.Address{0x03, 0x04, 0x05},
|
||||
Value: uint256.NewInt(99),
|
||||
Data: make([]byte, 50),
|
||||
BlobFeeCap: uint256.NewInt(15),
|
||||
BlobHashes: sidecar.BlobHashes(),
|
||||
Sidecar: sidecar,
|
||||
}
|
||||
return types.NewTx(tx)
|
||||
}
|
||||
|
||||
func randFieldElement() [32]byte {
|
||||
bytes := make([]byte, 32)
|
||||
_, err := rand.Read(bytes)
|
||||
if err != nil {
|
||||
panic("failed to get random field element")
|
||||
}
|
||||
var r fr.Element
|
||||
r.SetBytes(bytes)
|
||||
|
||||
return gokzg4844.SerializeScalar(r)
|
||||
}
|
||||
|
||||
func randBlob() kzg4844.Blob {
|
||||
var blob kzg4844.Blob
|
||||
for i := 0; i < len(blob); i += gokzg4844.SerializedScalarSize {
|
||||
fieldElementBytes := randFieldElement()
|
||||
copy(blob[i:i+gokzg4844.SerializedScalarSize], fieldElementBytes[:])
|
||||
}
|
||||
return blob
|
||||
}
|
||||
|
||||
func randomSidecar() *types.BlobTxSidecar {
|
||||
blob := randBlob()
|
||||
commitment, _ := kzg4844.BlobToCommitment(blob)
|
||||
proof, _ := kzg4844.ComputeBlobProof(blob, commitment)
|
||||
return &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{blob},
|
||||
Commitments: []kzg4844.Commitment{commitment},
|
||||
Proofs: []kzg4844.Proof{proof},
|
||||
}
|
||||
}
|
||||
|
||||
func emptySidecar() *types.BlobTxSidecar {
|
||||
return &types.BlobTxSidecar{
|
||||
Blobs: []kzg4844.Blob{emptyBlob},
|
||||
Commitments: []kzg4844.Commitment{emptyBlobCommit},
|
||||
Proofs: []kzg4844.Proof{emptyBlobProof},
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,13 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func postHertzConfig() *params.ChainConfig {
|
||||
func postHertzPreShanghaiConfig() *params.ChainConfig {
|
||||
config := *params.ParliaTestChainConfig
|
||||
config.ShanghaiTime = nil
|
||||
config.KeplerTime = nil
|
||||
config.FeynmanTime = nil
|
||||
config.FeynmanFixTime = nil
|
||||
config.CancunTime = nil
|
||||
return &config
|
||||
}
|
||||
|
||||
@@ -20,6 +25,11 @@ func preHertzConfig() *params.ChainConfig {
|
||||
config.BerlinBlock = nil
|
||||
config.HertzBlock = nil
|
||||
config.HertzfixBlock = nil
|
||||
config.ShanghaiTime = nil
|
||||
config.KeplerTime = nil
|
||||
config.FeynmanTime = nil
|
||||
config.FeynmanFixTime = nil
|
||||
config.CancunTime = nil
|
||||
return &config
|
||||
}
|
||||
|
||||
@@ -91,7 +101,7 @@ func TestSelfDestructGasPostHertz(t *testing.T) {
|
||||
// Expected gas is intrinsic + pc + cold load (due to legacy tx) + SelfDestructGas
|
||||
// i.e. No refund
|
||||
expectedGasUsed := params.TxGas + vm.GasQuickStep + params.ColdAccountAccessCostEIP2929 + params.SelfdestructGasEIP150
|
||||
TestGasUsage(t, postHertzConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
|
||||
TestGasUsage(t, postHertzPreShanghaiConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
|
||||
}
|
||||
|
||||
func TestSstoreGasPostHertz(t *testing.T) {
|
||||
@@ -103,7 +113,7 @@ func TestSstoreGasPostHertz(t *testing.T) {
|
||||
// Expected gas is intrinsic + 2*pushGas + cold load (due to legacy tx) + SstoreGas
|
||||
// i.e. No refund
|
||||
expectedGasUsed := params.TxGas + 2*vm.GasFastestStep + params.ColdSloadCostEIP2929 + params.SstoreSetGasEIP2200
|
||||
TestGasUsage(t, postHertzConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
|
||||
TestGasUsage(t, postHertzPreShanghaiConfig(), ethash.NewFaker(), bytecode, nil, 60_000, expectedGasUsed)
|
||||
}
|
||||
|
||||
func TestSstoreModifyGasPostHertz(t *testing.T) {
|
||||
@@ -120,7 +130,7 @@ func TestSstoreModifyGasPostHertz(t *testing.T) {
|
||||
// Expected gas is intrinsic + 2*pushGas + cold load (due to legacy tx) + SstoreReset (a->b such that a!=0)
|
||||
// i.e. No refund
|
||||
expectedGasUsed := params.TxGas + 2*vm.GasFastestStep + params.SstoreResetGasEIP2200
|
||||
TestGasUsage(t, postHertzConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsed)
|
||||
TestGasUsage(t, postHertzPreShanghaiConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsed)
|
||||
}
|
||||
|
||||
func TestSstoreClearGasPostHertz(t *testing.T) {
|
||||
@@ -137,5 +147,5 @@ func TestSstoreClearGasPostHertz(t *testing.T) {
|
||||
|
||||
// Expected gas is intrinsic + 2*pushGas + SstoreReset (a->b such that a!=0) - sstoreClearGasRefund
|
||||
expectedGasUsage := params.TxGas + 2*vm.GasFastestStep + params.SstoreResetGasEIP2200 - params.SstoreClearsScheduleRefundEIP3529
|
||||
TestGasUsage(t, postHertzConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsage)
|
||||
TestGasUsage(t, postHertzPreShanghaiConfig(), ethash.NewFaker(), bytecode, initialStorage, 60_000, expectedGasUsage)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
func newGwei(n int64) *big.Int {
|
||||
@@ -33,7 +33,7 @@ func TestGasUsage(t *testing.T, config *params.ChainConfig, engine consensus.Eng
|
||||
balanceBefore = big.NewInt(1000000000000000)
|
||||
gspec = &core.Genesis{
|
||||
Config: config,
|
||||
Alloc: core.GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
address: {Balance: balanceBefore},
|
||||
aa: {
|
||||
Code: bytecode,
|
||||
@@ -43,7 +43,7 @@ func TestGasUsage(t *testing.T, config *params.ChainConfig, engine consensus.Eng
|
||||
},
|
||||
},
|
||||
}
|
||||
genesis = gspec.MustCommit(db, trie.NewDatabase(db, nil))
|
||||
genesis = gspec.MustCommit(db, triedb.NewDatabase(db, nil))
|
||||
)
|
||||
|
||||
blocks, _ := core.GenerateChain(gspec.Config, genesis, engine, db, 1, func(i int, b *core.BlockGen) {
|
||||
@@ -62,7 +62,7 @@ func TestGasUsage(t *testing.T, config *params.ChainConfig, engine consensus.Eng
|
||||
|
||||
// Import the canonical chain
|
||||
diskdb := rawdb.NewMemoryDatabase()
|
||||
gspec.MustCommit(diskdb, trie.NewDatabase(diskdb, nil))
|
||||
gspec.MustCommit(diskdb, triedb.NewDatabase(diskdb, nil))
|
||||
|
||||
chain, err := core.NewBlockChain(diskdb, nil, gspec, nil, engine, vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
|
||||
@@ -27,7 +27,10 @@ type NewTxsEvent struct{ Txs []*types.Transaction }
|
||||
// ReannoTxsEvent is posted when a batch of local pending transactions exceed a specified duration.
|
||||
type ReannoTxsEvent struct{ Txs []*types.Transaction }
|
||||
|
||||
// NewMinedBlockEvent is posted when a block has been imported.
|
||||
// NewSealedBlockEvent is posted when a block has been sealed.
|
||||
type NewSealedBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// NewMinedBlockEvent is posted when a block has been mined.
|
||||
type NewMinedBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// RemovedLogsEvent is posted when a reorg happens
|
||||
@@ -50,3 +53,5 @@ type ChainSideEvent struct {
|
||||
}
|
||||
|
||||
type ChainHeadEvent struct{ Block *types.Block }
|
||||
|
||||
type HighestVerifiedBlockEvent struct{ Header *types.Header }
|
||||
|
||||
@@ -86,9 +86,16 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (b
|
||||
localTD = f.chain.GetTd(current.Hash(), current.Number.Uint64())
|
||||
externTd = f.chain.GetTd(extern.Hash(), extern.Number.Uint64())
|
||||
)
|
||||
if localTD == nil || externTd == nil {
|
||||
if localTD == nil {
|
||||
return false, errors.New("missing td")
|
||||
}
|
||||
if externTd == nil {
|
||||
ptd := f.chain.GetTd(extern.ParentHash, extern.Number.Uint64()-1)
|
||||
if ptd == nil {
|
||||
return false, consensus.ErrUnknownAncestor
|
||||
}
|
||||
externTd = new(big.Int).Add(ptd, extern.Difficulty)
|
||||
}
|
||||
// Accept the new header as the chain head if the transition
|
||||
// is already triggered. We assume all the headers after the
|
||||
// transition come from the trusted consensus layer.
|
||||
@@ -114,7 +121,12 @@ func (f *ForkChoice) ReorgNeeded(current *types.Header, extern *types.Header) (b
|
||||
if f.preserve != nil {
|
||||
currentPreserve, externPreserve = f.preserve(current), f.preserve(extern)
|
||||
}
|
||||
reorg = !currentPreserve && (externPreserve || f.rand.Float64() < 0.5)
|
||||
doubleSign := (extern.Coinbase == current.Coinbase)
|
||||
reorg = !currentPreserve && (externPreserve ||
|
||||
extern.Time < current.Time ||
|
||||
extern.Time == current.Time &&
|
||||
((doubleSign && extern.Hash().Cmp(current.Hash()) < 0) ||
|
||||
(!doubleSign && f.rand.Float64() < 0.5)))
|
||||
}
|
||||
return reorg, nil
|
||||
}
|
||||
|
||||
@@ -74,8 +74,10 @@ func TestCreation(t *testing.T) {
|
||||
{15049999, 0, ID{Hash: checksumToBytes(0x20c327fc), Next: 15050000}}, // Last Arrow Glacier block
|
||||
{15050000, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1681338455}}, // First Gray Glacier block
|
||||
{20000000, 1681338454, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 1681338455}}, // Last Gray Glacier block
|
||||
{20000000, 1681338455, ID{Hash: checksumToBytes(0xdce96c2d), Next: 0}}, // First Shanghai block
|
||||
{30000000, 2000000000, ID{Hash: checksumToBytes(0xdce96c2d), Next: 0}}, // Future Shanghai block
|
||||
{20000000, 1681338455, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}}, // First Shanghai block
|
||||
{30000000, 1710338134, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}}, // Last Shanghai block
|
||||
{40000000, 1710338135, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // First Cancun block
|
||||
{50000000, 2000000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}}, // Future Cancun block
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -94,6 +96,7 @@ func TestValidation(t *testing.T) {
|
||||
// Config that has not timestamp enabled
|
||||
legacyConfig := *params.MainnetChainConfig
|
||||
legacyConfig.ShanghaiTime = nil
|
||||
legacyConfig.CancunTime = nil
|
||||
|
||||
tests := []struct {
|
||||
config *params.ChainConfig
|
||||
@@ -166,14 +169,10 @@ func TestValidation(t *testing.T) {
|
||||
// at some future block 88888888, for itself, but past block for local. Local is incompatible.
|
||||
//
|
||||
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
|
||||
//
|
||||
// TODO(karalabe): This testcase will fail once mainnet gets timestamped forks, make legacy chain config
|
||||
{&legacyConfig, 88888888, 0, ID{Hash: checksumToBytes(0xf0afd0e3), Next: 88888888}, ErrLocalIncompatibleOrStale},
|
||||
|
||||
// Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing
|
||||
// fork) at block 7279999, before Petersburg. Local is incompatible.
|
||||
//
|
||||
// TODO(karalabe): This testcase will fail once mainnet gets timestamped forks, make legacy chain config
|
||||
{&legacyConfig, 7279999, 0, ID{Hash: checksumToBytes(0xa00bc324), Next: 7279999}, ErrLocalIncompatibleOrStale},
|
||||
|
||||
//------------------------------------
|
||||
@@ -250,34 +249,25 @@ func TestValidation(t *testing.T) {
|
||||
// Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces
|
||||
// also Shanghai, but it's not yet aware of Cancun (e.g. non updated node before the fork).
|
||||
// In this case we don't know if Cancun passed yet or not.
|
||||
//
|
||||
// TODO(karalabe): Enable this when Cancun is specced
|
||||
//{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: 0}, nil},
|
||||
{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0xdce96c2d), Next: 0}, nil},
|
||||
|
||||
// Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces
|
||||
// also Shanghai, and it's also aware of Cancun (e.g. updated node before the fork). We
|
||||
// don't know if Cancun passed yet (will pass) or not.
|
||||
//
|
||||
// TODO(karalabe): Enable this when Cancun is specced and update next timestamp
|
||||
//{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, nil},
|
||||
{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}, nil},
|
||||
|
||||
// Local is mainnet currently in Shanghai only (so it's aware of Cancun), remote announces
|
||||
// also Shanghai, and it's also aware of some random fork (e.g. misconfigured Cancun). As
|
||||
// neither forks passed at neither nodes, they may mismatch, but we still connect for now.
|
||||
//
|
||||
// TODO(karalabe): Enable this when Cancun is specced
|
||||
//{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0x71147644), Next: math.MaxUint64}, nil},
|
||||
{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(0xdce96c2d), Next: math.MaxUint64}, nil},
|
||||
|
||||
// Local is mainnet exactly on Cancun, remote announces Shanghai + knowledge about Cancun. Remote
|
||||
// is simply out of sync, accept.
|
||||
//
|
||||
// TODO(karalabe): Enable this when Cancun is specced, update local head and time, next timestamp
|
||||
// {params.MainnetChainConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, nil},
|
||||
{params.MainnetChainConfig, 21000000, 1710338135, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}, nil},
|
||||
|
||||
// Local is mainnet Cancun, remote announces Shanghai + knowledge about Cancun. Remote
|
||||
// is simply out of sync, accept.
|
||||
// TODO(karalabe): Enable this when Cancun is specced, update local head and time, next timestamp
|
||||
//{params.MainnetChainConfig, 21123456, 1678123456, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, nil},
|
||||
{params.MainnetChainConfig, 21123456, 1710338136, ID{Hash: checksumToBytes(0xdce96c2d), Next: 1710338135}, nil},
|
||||
|
||||
// Local is mainnet Prague, remote announces Shanghai + knowledge about Cancun. Remote
|
||||
// is definitely out of sync. It may or may not need the Prague update, we don't know yet.
|
||||
@@ -286,9 +276,7 @@ func TestValidation(t *testing.T) {
|
||||
//{params.MainnetChainConfig, 0, 0, ID{Hash: checksumToBytes(0x3edd5b10), Next: 4370000}, nil},
|
||||
|
||||
// Local is mainnet Shanghai, remote announces Cancun. Local is out of sync, accept.
|
||||
//
|
||||
// TODO(karalabe): Enable this when Cancun is specced, update remote checksum
|
||||
//{params.MainnetChainConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x00000000), Next: 0}, nil},
|
||||
{params.MainnetChainConfig, 21000000, 1700000000, ID{Hash: checksumToBytes(0x9f3d2254), Next: 0}, nil},
|
||||
|
||||
// Local is mainnet Shanghai, remote announces Cancun, but is not aware of Prague. Local
|
||||
// out of sync. Local also knows about a future fork, but that is uncertain yet.
|
||||
@@ -298,9 +286,7 @@ func TestValidation(t *testing.T) {
|
||||
|
||||
// Local is mainnet Cancun. remote announces Shanghai but is not aware of further forks.
|
||||
// Remote needs software update.
|
||||
//
|
||||
// TODO(karalabe): Enable this when Cancun is specced, update local head and time
|
||||
//{params.MainnetChainConfig, 21000000, 1678000000, ID{Hash: checksumToBytes(0x71147644), Next: 0}, ErrRemoteStale},
|
||||
{params.MainnetChainConfig, 21000000, 1710338135, ID{Hash: checksumToBytes(0xdce96c2d), Next: 0}, ErrRemoteStale},
|
||||
|
||||
// Local is mainnet Shanghai, and isn't aware of more forks. Remote announces Shanghai +
|
||||
// 0xffffffff. Local needs software update, reject.
|
||||
@@ -308,24 +294,20 @@ func TestValidation(t *testing.T) {
|
||||
|
||||
// Local is mainnet Shanghai, and is aware of Cancun. Remote announces Cancun +
|
||||
// 0xffffffff. Local needs software update, reject.
|
||||
//
|
||||
// TODO(karalabe): Enable this when Cancun is specced, update remote checksum
|
||||
//{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(checksumUpdate(0x00000000, math.MaxUint64)), Next: 0}, ErrLocalIncompatibleOrStale},
|
||||
{params.MainnetChainConfig, 20000000, 1668000000, ID{Hash: checksumToBytes(checksumUpdate(0x9f3d2254, math.MaxUint64)), Next: 0}, ErrLocalIncompatibleOrStale},
|
||||
|
||||
// Local is mainnet Shanghai, remote is random Shanghai.
|
||||
{params.MainnetChainConfig, 20000000, 1681338455, ID{Hash: checksumToBytes(0x12345678), Next: 0}, ErrLocalIncompatibleOrStale},
|
||||
|
||||
// Local is mainnet Shanghai, far in the future. Remote announces Gopherium (non existing fork)
|
||||
// Local is mainnet Cancun, far in the future. Remote announces Gopherium (non existing fork)
|
||||
// at some future timestamp 8888888888, for itself, but past block for local. Local is incompatible.
|
||||
//
|
||||
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
|
||||
{params.MainnetChainConfig, 88888888, 8888888888, ID{Hash: checksumToBytes(0xdce96c2d), Next: 8888888888}, ErrLocalIncompatibleOrStale},
|
||||
{params.MainnetChainConfig, 88888888, 8888888888, ID{Hash: checksumToBytes(0x9f3d2254), Next: 8888888888}, ErrLocalIncompatibleOrStale},
|
||||
|
||||
// Local is mainnet Shanghai. Remote is also in Shanghai, but announces Gopherium (non existing
|
||||
// fork) at timestamp 1668000000, before Cancun. Local is incompatible.
|
||||
//
|
||||
// TODO(karalabe): Enable this when Cancun is specced
|
||||
//{params.MainnetChainConfig, 20999999, 1677999999, ID{Hash: checksumToBytes(0x71147644), Next: 1678000000}, ErrLocalIncompatibleOrStale},
|
||||
{params.MainnetChainConfig, 20999999, 1699999999, ID{Hash: checksumToBytes(0x71147644), Next: 1700000000}, ErrLocalIncompatibleOrStale},
|
||||
}
|
||||
genesis := core.DefaultGenesisBlock().ToBlock()
|
||||
for i, tt := range tests {
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
@@ -18,21 +19,21 @@ var _ = (*genesisSpecMarshaling)(nil)
|
||||
// MarshalJSON marshals as JSON.
|
||||
func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData hexutil.Bytes `json:"extraData"`
|
||||
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash common.Hash `json:"mixHash"`
|
||||
Coinbase common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
|
||||
Number math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData hexutil.Bytes `json:"extraData"`
|
||||
GasLimit math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash common.Hash `json:"mixHash"`
|
||||
Coinbase common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
|
||||
Number math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
}
|
||||
var enc Genesis
|
||||
enc.Config = g.Config
|
||||
@@ -44,7 +45,7 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||
enc.Mixhash = g.Mixhash
|
||||
enc.Coinbase = g.Coinbase
|
||||
if g.Alloc != nil {
|
||||
enc.Alloc = make(map[common.UnprefixedAddress]GenesisAccount, len(g.Alloc))
|
||||
enc.Alloc = make(map[common.UnprefixedAddress]types.Account, len(g.Alloc))
|
||||
for k, v := range g.Alloc {
|
||||
enc.Alloc[common.UnprefixedAddress(k)] = v
|
||||
}
|
||||
@@ -61,21 +62,21 @@ func (g Genesis) MarshalJSON() ([]byte, error) {
|
||||
// UnmarshalJSON unmarshals from JSON.
|
||||
func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||
type Genesis struct {
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData *hexutil.Bytes `json:"extraData"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash *common.Hash `json:"mixHash"`
|
||||
Coinbase *common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount `json:"alloc" gencodec:"required"`
|
||||
Number *math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash *common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
Config *params.ChainConfig `json:"config"`
|
||||
Nonce *math.HexOrDecimal64 `json:"nonce"`
|
||||
Timestamp *math.HexOrDecimal64 `json:"timestamp"`
|
||||
ExtraData *hexutil.Bytes `json:"extraData"`
|
||||
GasLimit *math.HexOrDecimal64 `json:"gasLimit" gencodec:"required"`
|
||||
Difficulty *math.HexOrDecimal256 `json:"difficulty" gencodec:"required"`
|
||||
Mixhash *common.Hash `json:"mixHash"`
|
||||
Coinbase *common.Address `json:"coinbase"`
|
||||
Alloc map[common.UnprefixedAddress]types.Account `json:"alloc" gencodec:"required"`
|
||||
Number *math.HexOrDecimal64 `json:"number"`
|
||||
GasUsed *math.HexOrDecimal64 `json:"gasUsed"`
|
||||
ParentHash *common.Hash `json:"parentHash"`
|
||||
BaseFee *math.HexOrDecimal256 `json:"baseFeePerGas"`
|
||||
ExcessBlobGas *math.HexOrDecimal64 `json:"excessBlobGas"`
|
||||
BlobGasUsed *math.HexOrDecimal64 `json:"blobGasUsed"`
|
||||
}
|
||||
var dec Genesis
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
@@ -110,7 +111,7 @@ func (g *Genesis) UnmarshalJSON(input []byte) error {
|
||||
if dec.Alloc == nil {
|
||||
return errors.New("missing required field 'alloc' for Genesis")
|
||||
}
|
||||
g.Alloc = make(GenesisAlloc, len(dec.Alloc))
|
||||
g.Alloc = make(types.GenesisAlloc, len(dec.Alloc))
|
||||
for k, v := range dec.Alloc {
|
||||
g.Alloc[common.Address(k)] = v
|
||||
}
|
||||
|
||||
154
core/genesis.go
154
core/genesis.go
@@ -18,7 +18,6 @@ package core
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -37,15 +36,21 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
//go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
|
||||
//go:generate go run github.com/fjl/gencodec -type GenesisAccount -field-override genesisAccountMarshaling -out gen_genesis_account.go
|
||||
|
||||
var errGenesisNoConfig = errors.New("genesis has no chain configuration")
|
||||
|
||||
// Deprecated: use types.GenesisAccount instead.
|
||||
type GenesisAccount = types.Account
|
||||
|
||||
// Deprecated: use types.GenesisAlloc instead.
|
||||
type GenesisAlloc = types.GenesisAlloc
|
||||
|
||||
// Genesis specifies the header fields, state of a genesis block. It also defines hard
|
||||
// fork switch-over blocks through the chain configuration.
|
||||
type Genesis struct {
|
||||
@@ -57,7 +62,7 @@ type Genesis struct {
|
||||
Difficulty *big.Int `json:"difficulty" gencodec:"required"`
|
||||
Mixhash common.Hash `json:"mixHash"`
|
||||
Coinbase common.Address `json:"coinbase"`
|
||||
Alloc GenesisAlloc `json:"alloc" gencodec:"required"`
|
||||
Alloc types.GenesisAlloc `json:"alloc" gencodec:"required"`
|
||||
|
||||
// These fields are used for consensus tests. Please don't use them
|
||||
// in actual genesis blocks.
|
||||
@@ -107,29 +112,14 @@ func ReadGenesis(db ethdb.Database) (*Genesis, error) {
|
||||
return &genesis, nil
|
||||
}
|
||||
|
||||
// GenesisAlloc specifies the initial state that is part of the genesis block.
|
||||
type GenesisAlloc map[common.Address]GenesisAccount
|
||||
|
||||
func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
|
||||
m := make(map[common.UnprefixedAddress]GenesisAccount)
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
*ga = make(GenesisAlloc)
|
||||
for addr, a := range m {
|
||||
(*ga)[common.Address(addr)] = a
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// hash computes the state root according to the genesis specification.
|
||||
func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
|
||||
// hashAlloc computes the state root according to the genesis specification.
|
||||
func hashAlloc(ga *types.GenesisAlloc, isVerkle bool) (common.Hash, error) {
|
||||
// If a genesis-time verkle trie is requested, create a trie config
|
||||
// with the verkle trie enabled so that the tree can be initialized
|
||||
// as such.
|
||||
var config *trie.Config
|
||||
var config *triedb.Config
|
||||
if isVerkle {
|
||||
config = &trie.Config{
|
||||
config = &triedb.Config{
|
||||
PathDB: pathdb.Defaults,
|
||||
IsVerkle: true,
|
||||
}
|
||||
@@ -156,13 +146,13 @@ func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
|
||||
return root, err
|
||||
}
|
||||
|
||||
// flush is very similar with hash, but the main difference is all the generated
|
||||
// flushAlloc is very similar with hash, but the main difference is all the generated
|
||||
// states will be persisted into the given database. Also, the genesis state
|
||||
// specification will be flushed as well.
|
||||
func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhash common.Hash) error {
|
||||
trieConfig := triedb.Config()
|
||||
if trieConfig != nil {
|
||||
trieConfig.NoTries = false
|
||||
func flushAlloc(ga *types.GenesisAlloc, db ethdb.Database, triedb *triedb.Database, blockhash common.Hash) error {
|
||||
triedbConfig := triedb.Config()
|
||||
if triedbConfig != nil {
|
||||
triedbConfig.NoTries = false
|
||||
}
|
||||
statedb, err := state.New(types.EmptyRootHash, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
||||
if err != nil {
|
||||
@@ -198,15 +188,6 @@ func (ga *GenesisAlloc) flush(db ethdb.Database, triedb *trie.Database, blockhas
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenesisAccount is an account in the state of the genesis block.
|
||||
type GenesisAccount struct {
|
||||
Code []byte `json:"code,omitempty"`
|
||||
Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
|
||||
Balance *big.Int `json:"balance" gencodec:"required"`
|
||||
Nonce uint64 `json:"nonce,omitempty"`
|
||||
PrivateKey []byte `json:"secretKey,omitempty"` // for tests
|
||||
}
|
||||
|
||||
// field type overrides for gencodec
|
||||
type genesisSpecMarshaling struct {
|
||||
Nonce math.HexOrDecimal64
|
||||
@@ -216,40 +197,12 @@ type genesisSpecMarshaling struct {
|
||||
GasUsed math.HexOrDecimal64
|
||||
Number math.HexOrDecimal64
|
||||
Difficulty *math.HexOrDecimal256
|
||||
Alloc map[common.UnprefixedAddress]GenesisAccount
|
||||
Alloc map[common.UnprefixedAddress]types.Account
|
||||
BaseFee *math.HexOrDecimal256
|
||||
ExcessBlobGas *math.HexOrDecimal64
|
||||
BlobGasUsed *math.HexOrDecimal64
|
||||
}
|
||||
|
||||
type genesisAccountMarshaling struct {
|
||||
Code hexutil.Bytes
|
||||
Balance *math.HexOrDecimal256
|
||||
Nonce math.HexOrDecimal64
|
||||
Storage map[storageJSON]storageJSON
|
||||
PrivateKey hexutil.Bytes
|
||||
}
|
||||
|
||||
// storageJSON represents a 256 bit byte array, but allows less than 256 bits when
|
||||
// unmarshaling from hex.
|
||||
type storageJSON common.Hash
|
||||
|
||||
func (h *storageJSON) UnmarshalText(text []byte) error {
|
||||
text = bytes.TrimPrefix(text, []byte("0x"))
|
||||
if len(text) > 64 {
|
||||
return fmt.Errorf("too many hex characters in storage key/value %q", text)
|
||||
}
|
||||
offset := len(h) - len(text)/2 // pad on the left
|
||||
if _, err := hex.Decode(h[offset:], text); err != nil {
|
||||
return fmt.Errorf("invalid hex storage key/value %q", text)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h storageJSON) MarshalText() ([]byte, error) {
|
||||
return hexutil.Bytes(h[:]).MarshalText()
|
||||
}
|
||||
|
||||
// GenesisMismatchError is raised when trying to overwrite an existing
|
||||
// genesis block with an incompatible one.
|
||||
type GenesisMismatchError struct {
|
||||
@@ -263,11 +216,8 @@ func (e *GenesisMismatchError) Error() string {
|
||||
// ChainOverrides contains the changes to chain config
|
||||
// Typically, these modifications involve hardforks that are not enabled on the BSC mainnet, intended for testing purposes.
|
||||
type ChainOverrides struct {
|
||||
OverrideShanghai *uint64
|
||||
OverrideKepler *uint64
|
||||
OverrideCancun *uint64
|
||||
OverrideVerkle *uint64
|
||||
OverrideFeynman *uint64
|
||||
OverrideBohr *uint64
|
||||
OverrideVerkle *uint64
|
||||
}
|
||||
|
||||
// SetupGenesisBlock writes or updates the genesis block in db.
|
||||
@@ -283,31 +233,22 @@ type ChainOverrides struct {
|
||||
// error is a *params.ConfigCompatError and the new, unwritten config is returned.
|
||||
//
|
||||
// The returned chain configuration is never nil.
|
||||
func SetupGenesisBlock(db ethdb.Database, triedb *trie.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
||||
func SetupGenesisBlock(db ethdb.Database, triedb *triedb.Database, genesis *Genesis) (*params.ChainConfig, common.Hash, error) {
|
||||
return SetupGenesisBlockWithOverride(db, triedb, genesis, nil)
|
||||
}
|
||||
|
||||
func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, genesis *Genesis, overrides *ChainOverrides) (*params.ChainConfig, common.Hash, error) {
|
||||
func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *triedb.Database, genesis *Genesis, overrides *ChainOverrides) (*params.ChainConfig, common.Hash, error) {
|
||||
if genesis != nil && genesis.Config == nil {
|
||||
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
|
||||
}
|
||||
applyOverrides := func(config *params.ChainConfig) {
|
||||
if config != nil {
|
||||
if overrides != nil && overrides.OverrideShanghai != nil {
|
||||
config.ShanghaiTime = overrides.OverrideShanghai
|
||||
}
|
||||
if overrides != nil && overrides.OverrideKepler != nil {
|
||||
config.KeplerTime = overrides.OverrideKepler
|
||||
}
|
||||
if overrides != nil && overrides.OverrideCancun != nil {
|
||||
config.CancunTime = overrides.OverrideCancun
|
||||
if overrides != nil && overrides.OverrideBohr != nil {
|
||||
config.BohrTime = overrides.OverrideBohr
|
||||
}
|
||||
if overrides != nil && overrides.OverrideVerkle != nil {
|
||||
config.VerkleTime = overrides.OverrideVerkle
|
||||
}
|
||||
if overrides != nil && overrides.OverrideFeynman != nil {
|
||||
config.FeynmanTime = overrides.OverrideFeynman
|
||||
}
|
||||
}
|
||||
}
|
||||
// Just commit the new block if there is no stored genesis block.
|
||||
@@ -332,7 +273,7 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, triedb *trie.Database, gen
|
||||
// is initialized with an external ancient store. Commit genesis state
|
||||
// in this case.
|
||||
header := rawdb.ReadHeader(db, stored, 0)
|
||||
if header.Root != types.EmptyRootHash && !triedb.Initialized(header.Root) {
|
||||
if header.Root != types.EmptyRootHash && !triedb.Initialized(header.Root) && !triedb.Config().NoTries {
|
||||
if genesis == nil {
|
||||
genesis = DefaultBSCGenesisBlock()
|
||||
}
|
||||
@@ -453,7 +394,7 @@ func (g *Genesis) IsVerkle() bool {
|
||||
|
||||
// ToBlock returns the genesis block according to genesis specification.
|
||||
func (g *Genesis) ToBlock() *types.Block {
|
||||
root, err := g.Alloc.hash(g.IsVerkle())
|
||||
root, err := hashAlloc(&g.Alloc, g.IsVerkle())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -496,10 +437,17 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||
withdrawals = make([]*types.Withdrawal, 0)
|
||||
}
|
||||
if conf.IsCancun(num, g.Timestamp) {
|
||||
if conf.Parlia != nil {
|
||||
head.WithdrawalsHash = &types.EmptyWithdrawalsHash
|
||||
}
|
||||
|
||||
// EIP-4788: The parentBeaconBlockRoot of the genesis block is always
|
||||
// the zero hash. This is because the genesis block does not have a parent
|
||||
// by definition.
|
||||
head.ParentBeaconRoot = new(common.Hash)
|
||||
if conf.Parlia == nil || conf.IsBohr(num, g.Timestamp) {
|
||||
head.ParentBeaconRoot = new(common.Hash)
|
||||
}
|
||||
|
||||
// EIP-4844 fields
|
||||
head.ExcessBlobGas = g.ExcessBlobGas
|
||||
head.BlobGasUsed = g.BlobGasUsed
|
||||
@@ -516,7 +464,7 @@ func (g *Genesis) ToBlock() *types.Block {
|
||||
|
||||
// Commit writes the block and state of a genesis specification to the database.
|
||||
// The block is committed as the canonical head block.
|
||||
func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block, error) {
|
||||
func (g *Genesis) Commit(db ethdb.Database, triedb *triedb.Database) (*types.Block, error) {
|
||||
block := g.ToBlock()
|
||||
if block.Number().Sign() != 0 {
|
||||
return nil, errors.New("can't commit genesis block with number > 0")
|
||||
@@ -531,26 +479,26 @@ func (g *Genesis) Commit(db ethdb.Database, triedb *trie.Database) (*types.Block
|
||||
if config.Clique != nil && len(block.Extra()) < 32+crypto.SignatureLength {
|
||||
return nil, errors.New("can't start clique chain without signers")
|
||||
}
|
||||
// All the checks has passed, flush the states derived from the genesis
|
||||
// All the checks has passed, flushAlloc the states derived from the genesis
|
||||
// specification as well as the specification itself into the provided
|
||||
// database.
|
||||
if err := g.Alloc.flush(db, triedb, block.Hash()); err != nil {
|
||||
if err := flushAlloc(&g.Alloc, db, triedb, block.Hash()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
|
||||
rawdb.WriteBlock(db, block)
|
||||
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
|
||||
rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
|
||||
rawdb.WriteHeadBlockHash(db, block.Hash())
|
||||
rawdb.WriteHeadFastBlockHash(db, block.Hash())
|
||||
rawdb.WriteHeadHeaderHash(db, block.Hash())
|
||||
rawdb.WriteTd(db.BlockStore(), block.Hash(), block.NumberU64(), block.Difficulty())
|
||||
rawdb.WriteBlock(db.BlockStore(), block)
|
||||
rawdb.WriteReceipts(db.BlockStore(), block.Hash(), block.NumberU64(), nil)
|
||||
rawdb.WriteCanonicalHash(db.BlockStore(), block.Hash(), block.NumberU64())
|
||||
rawdb.WriteHeadBlockHash(db.BlockStore(), block.Hash())
|
||||
rawdb.WriteHeadFastBlockHash(db.BlockStore(), block.Hash())
|
||||
rawdb.WriteHeadHeaderHash(db.BlockStore(), block.Hash())
|
||||
rawdb.WriteChainConfig(db, block.Hash(), config)
|
||||
return block, nil
|
||||
}
|
||||
|
||||
// MustCommit writes the genesis block and state to db, panicking on error.
|
||||
// The block is committed as the canonical head block.
|
||||
func (g *Genesis) MustCommit(db ethdb.Database, triedb *trie.Database) *types.Block {
|
||||
func (g *Genesis) MustCommit(db ethdb.Database, triedb *triedb.Database) *types.Block {
|
||||
block, err := g.Commit(db, triedb)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
@@ -613,7 +561,7 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
||||
GasLimit: gasLimit,
|
||||
BaseFee: big.NewInt(params.InitialBaseFee),
|
||||
Difficulty: big.NewInt(1),
|
||||
Alloc: map[common.Address]GenesisAccount{
|
||||
Alloc: map[common.Address]types.Account{
|
||||
common.BytesToAddress([]byte{1}): {Balance: big.NewInt(1)}, // ECRecover
|
||||
common.BytesToAddress([]byte{2}): {Balance: big.NewInt(1)}, // SHA256
|
||||
common.BytesToAddress([]byte{3}): {Balance: big.NewInt(1)}, // RIPEMD
|
||||
@@ -626,12 +574,12 @@ func DeveloperGenesisBlock(gasLimit uint64, faucet *common.Address) *Genesis {
|
||||
},
|
||||
}
|
||||
if faucet != nil {
|
||||
genesis.Alloc[*faucet] = GenesisAccount{Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}
|
||||
genesis.Alloc[*faucet] = types.Account{Balance: new(big.Int).Sub(new(big.Int).Lsh(big.NewInt(1), 256), big.NewInt(9))}
|
||||
}
|
||||
return genesis
|
||||
}
|
||||
|
||||
func decodePrealloc(data string) GenesisAlloc {
|
||||
func decodePrealloc(data string) types.GenesisAlloc {
|
||||
var p []struct {
|
||||
Addr *big.Int
|
||||
Balance *big.Int
|
||||
@@ -647,9 +595,9 @@ func decodePrealloc(data string) GenesisAlloc {
|
||||
if err := rlp.NewStream(strings.NewReader(data), 0).Decode(&p); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ga := make(GenesisAlloc, len(p))
|
||||
ga := make(types.GenesisAlloc, len(p))
|
||||
for _, account := range p {
|
||||
acc := GenesisAccount{Balance: account.Balance}
|
||||
acc := types.Account{Balance: account.Balance}
|
||||
if account.Misc != nil {
|
||||
acc.Nonce = account.Misc.Nonce
|
||||
acc.Code = account.Misc.Code
|
||||
|
||||
@@ -27,11 +27,12 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"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"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/triedb/pathdb"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
"github.com/ethereum/go-ethereum/triedb/pathdb"
|
||||
)
|
||||
|
||||
func TestSetupGenesis(t *testing.T) {
|
||||
@@ -44,7 +45,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
customghash = common.HexToHash("0x89c99d90b79719238d2645c7642f2c9295246e80775b38cfd162b696817fbd50")
|
||||
customg = Genesis{
|
||||
Config: ¶ms.ChainConfig{HomesteadBlock: big.NewInt(3)},
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
||||
},
|
||||
}
|
||||
@@ -62,7 +63,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
{
|
||||
name: "genesis without ChainConfig",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), new(Genesis))
|
||||
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), new(Genesis))
|
||||
},
|
||||
wantErr: errGenesisNoConfig,
|
||||
wantConfig: params.AllEthashProtocolChanges,
|
||||
@@ -70,7 +71,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
{
|
||||
name: "no block in DB, genesis == nil",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||
},
|
||||
wantHash: params.BSCGenesisHash,
|
||||
wantConfig: params.BSCChainConfig,
|
||||
@@ -78,8 +79,8 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
{
|
||||
name: "mainnet block in DB, genesis == nil",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
DefaultGenesisBlock().MustCommit(db, trie.NewDatabase(db, newDbConfig(scheme)))
|
||||
return SetupGenesisBlock(db, trie.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||
DefaultGenesisBlock().MustCommit(db, triedb.NewDatabase(db, newDbConfig(scheme)))
|
||||
return SetupGenesisBlock(db, triedb.NewDatabase(db, newDbConfig(scheme)), nil)
|
||||
},
|
||||
wantHash: params.MainnetGenesisHash,
|
||||
wantConfig: params.MainnetChainConfig,
|
||||
@@ -87,7 +88,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
{
|
||||
name: "custom block in DB, genesis == nil",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
||||
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||
customg.Commit(db, tdb)
|
||||
return SetupGenesisBlock(db, tdb, nil)
|
||||
},
|
||||
@@ -97,7 +98,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
{
|
||||
name: "custom block in DB, genesis == chapel",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
||||
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||
customg.Commit(db, tdb)
|
||||
return SetupGenesisBlock(db, tdb, DefaultChapelGenesisBlock())
|
||||
},
|
||||
@@ -108,7 +109,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
{
|
||||
name: "compatible config in DB",
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
||||
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||
oldcustomg.Commit(db, tdb)
|
||||
return SetupGenesisBlock(db, tdb, &customg)
|
||||
},
|
||||
@@ -120,7 +121,7 @@ func testSetupGenesis(t *testing.T, scheme string) {
|
||||
fn: func(db ethdb.Database) (*params.ChainConfig, common.Hash, error) {
|
||||
// Commit the 'old' genesis block with Homestead transition at #2.
|
||||
// Advance to block #4, past the homestead transition block of customg.
|
||||
tdb := trie.NewDatabase(db, newDbConfig(scheme))
|
||||
tdb := triedb.NewDatabase(db, newDbConfig(scheme))
|
||||
oldcustomg.Commit(db, tdb)
|
||||
|
||||
bc, _ := NewBlockChain(db, DefaultCacheConfigWithScheme(scheme), &oldcustomg, nil, ethash.NewFullFaker(), vm.Config{}, nil, nil)
|
||||
@@ -177,7 +178,7 @@ func TestGenesisHashes(t *testing.T) {
|
||||
} {
|
||||
// Test via MustCommit
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
if have := c.genesis.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults)).Hash(); have != c.want {
|
||||
if have := c.genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults)).Hash(); have != c.want {
|
||||
t.Errorf("case: %d a), want: %s, got: %s", i, c.want.Hex(), have.Hex())
|
||||
}
|
||||
// Test via ToBlock
|
||||
@@ -195,7 +196,7 @@ func TestGenesis_Commit(t *testing.T) {
|
||||
}
|
||||
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
genesisBlock := genesis.MustCommit(db, trie.NewDatabase(db, trie.HashDefaults))
|
||||
genesisBlock := genesis.MustCommit(db, triedb.NewDatabase(db, triedb.HashDefaults))
|
||||
|
||||
if genesis.Difficulty != nil {
|
||||
t.Fatalf("assumption wrong")
|
||||
@@ -217,16 +218,16 @@ func TestGenesis_Commit(t *testing.T) {
|
||||
func TestReadWriteGenesisAlloc(t *testing.T) {
|
||||
var (
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
alloc = &GenesisAlloc{
|
||||
alloc = &types.GenesisAlloc{
|
||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
||||
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
|
||||
}
|
||||
hash, _ = alloc.hash(false)
|
||||
hash, _ = hashAlloc(alloc, false)
|
||||
)
|
||||
blob, _ := json.Marshal(alloc)
|
||||
rawdb.WriteGenesisStateSpec(db, hash, blob)
|
||||
|
||||
var reload GenesisAlloc
|
||||
var reload types.GenesisAlloc
|
||||
err := reload.UnmarshalJSON(rawdb.ReadGenesisStateSpec(db, hash))
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to load genesis state %v", err)
|
||||
@@ -270,11 +271,11 @@ func TestConfigOrDefault(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func newDbConfig(scheme string) *trie.Config {
|
||||
func newDbConfig(scheme string) *triedb.Config {
|
||||
if scheme == rawdb.HashScheme {
|
||||
return trie.HashDefaults
|
||||
return triedb.HashDefaults
|
||||
}
|
||||
return &trie.Config{PathDB: pathdb.Defaults}
|
||||
return &triedb.Config{PathDB: pathdb.Defaults}
|
||||
}
|
||||
|
||||
func TestVerkleGenesisCommit(t *testing.T) {
|
||||
@@ -312,7 +313,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||
Config: verkleConfig,
|
||||
Timestamp: verkleTime,
|
||||
Difficulty: big.NewInt(0),
|
||||
Alloc: GenesisAlloc{
|
||||
Alloc: types.GenesisAlloc{
|
||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
||||
},
|
||||
}
|
||||
@@ -324,7 +325,7 @@ func TestVerkleGenesisCommit(t *testing.T) {
|
||||
}
|
||||
|
||||
db := rawdb.NewMemoryDatabase()
|
||||
triedb := trie.NewDatabase(db, &trie.Config{IsVerkle: true, PathDB: pathdb.Defaults})
|
||||
triedb := triedb.NewDatabase(db, &triedb.Config{IsVerkle: true, PathDB: pathdb.Defaults})
|
||||
block := genesis.MustCommit(db, triedb)
|
||||
if !bytes.Equal(block.Root().Bytes(), expected) {
|
||||
t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
|
||||
|
||||
@@ -172,9 +172,9 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error {
|
||||
// pile them onto the existing chain. Otherwise, do the necessary
|
||||
// reorgs.
|
||||
var (
|
||||
first = headers[0]
|
||||
last = headers[len(headers)-1]
|
||||
batch = hc.chainDb.NewBatch()
|
||||
first = headers[0]
|
||||
last = headers[len(headers)-1]
|
||||
blockBatch = hc.chainDb.BlockStore().NewBatch()
|
||||
)
|
||||
if first.ParentHash != hc.currentHeaderHash {
|
||||
// Delete any canonical number assignments above the new head
|
||||
@@ -183,7 +183,7 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error {
|
||||
if hash == (common.Hash{}) {
|
||||
break
|
||||
}
|
||||
rawdb.DeleteCanonicalHash(batch, i)
|
||||
rawdb.DeleteCanonicalHash(blockBatch, i)
|
||||
}
|
||||
// Overwrite any stale canonical number assignments, going
|
||||
// backwards from the first header in this import until the
|
||||
@@ -194,7 +194,7 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error {
|
||||
headHash = header.Hash()
|
||||
)
|
||||
for rawdb.ReadCanonicalHash(hc.chainDb, headNumber) != headHash {
|
||||
rawdb.WriteCanonicalHash(batch, headHash, headNumber)
|
||||
rawdb.WriteCanonicalHash(blockBatch, headHash, headNumber)
|
||||
if headNumber == 0 {
|
||||
break // It shouldn't be reached
|
||||
}
|
||||
@@ -209,16 +209,16 @@ func (hc *HeaderChain) Reorg(headers []*types.Header) error {
|
||||
for i := 0; i < len(headers)-1; i++ {
|
||||
hash := headers[i+1].ParentHash // Save some extra hashing
|
||||
num := headers[i].Number.Uint64()
|
||||
rawdb.WriteCanonicalHash(batch, hash, num)
|
||||
rawdb.WriteHeadHeaderHash(batch, hash)
|
||||
rawdb.WriteCanonicalHash(blockBatch, hash, num)
|
||||
rawdb.WriteHeadHeaderHash(blockBatch, hash)
|
||||
}
|
||||
// Write the last header
|
||||
hash := headers[len(headers)-1].Hash()
|
||||
num := headers[len(headers)-1].Number.Uint64()
|
||||
rawdb.WriteCanonicalHash(batch, hash, num)
|
||||
rawdb.WriteHeadHeaderHash(batch, hash)
|
||||
rawdb.WriteCanonicalHash(blockBatch, hash, num)
|
||||
rawdb.WriteHeadHeaderHash(blockBatch, hash)
|
||||
|
||||
if err := batch.Write(); err != nil {
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
return err
|
||||
}
|
||||
// Last step update all in-memory head header markers
|
||||
@@ -244,7 +244,7 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||
newTD = new(big.Int).Set(ptd) // Total difficulty of inserted chain
|
||||
inserted []rawdb.NumberHash // Ephemeral lookup of number/hash for the chain
|
||||
parentKnown = true // Set to true to force hc.HasHeader check the first iteration
|
||||
batch = hc.chainDb.NewBatch()
|
||||
blockBatch = hc.chainDb.BlockStore().NewBatch()
|
||||
)
|
||||
for i, header := range headers {
|
||||
var hash common.Hash
|
||||
@@ -264,10 +264,10 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||
alreadyKnown := parentKnown && hc.HasHeader(hash, number)
|
||||
if !alreadyKnown {
|
||||
// Irrelevant of the canonical status, write the TD and header to the database.
|
||||
rawdb.WriteTd(batch, hash, number, newTD)
|
||||
rawdb.WriteTd(blockBatch, hash, number, newTD)
|
||||
hc.tdCache.Add(hash, new(big.Int).Set(newTD))
|
||||
|
||||
rawdb.WriteHeader(batch, header)
|
||||
rawdb.WriteHeader(blockBatch, header)
|
||||
inserted = append(inserted, rawdb.NumberHash{Number: number, Hash: hash})
|
||||
hc.headerCache.Add(hash, header)
|
||||
hc.numberCache.Add(hash, number)
|
||||
@@ -280,7 +280,7 @@ func (hc *HeaderChain) WriteHeaders(headers []*types.Header) (int, error) {
|
||||
return 0, errors.New("aborted")
|
||||
}
|
||||
// Commit to disk!
|
||||
if err := batch.Write(); err != nil {
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to write headers", "error", err)
|
||||
}
|
||||
return len(inserted), nil
|
||||
@@ -436,6 +436,14 @@ func (hc *HeaderChain) GetHighestVerifiedHeader() *types.Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hc *HeaderChain) GetVerifiedBlockByHash(hash common.Hash) *types.Header {
|
||||
return hc.GetHeaderByHash(hash)
|
||||
}
|
||||
|
||||
func (hc *HeaderChain) ChasingHead() *types.Header {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
|
||||
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
|
||||
// number of blocks to be individually checked before we reach the canonical chain.
|
||||
@@ -638,7 +646,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||
}
|
||||
var (
|
||||
parentHash common.Hash
|
||||
batch = hc.chainDb.NewBatch()
|
||||
blockBatch = hc.chainDb.BlockStore().NewBatch()
|
||||
origin = true
|
||||
)
|
||||
done := func(header *types.Header) bool {
|
||||
@@ -664,7 +672,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||
// first then remove the relative data from the database.
|
||||
//
|
||||
// Update head first(head fast block, head full block) before deleting the data.
|
||||
markerBatch := hc.chainDb.NewBatch()
|
||||
markerBatch := hc.chainDb.BlockStore().NewBatch()
|
||||
if updateFn != nil {
|
||||
newHead, force := updateFn(markerBatch, parent)
|
||||
if force && ((headTime > 0 && newHead.Time < headTime) || (headTime == 0 && newHead.Number.Uint64() < headBlock)) {
|
||||
@@ -687,7 +695,7 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||
// we don't end up with dangling daps in the database
|
||||
var nums []uint64
|
||||
if origin {
|
||||
for n := num + 1; len(rawdb.ReadAllHashes(hc.chainDb, n)) > 0; n++ {
|
||||
for n := num + 1; len(rawdb.ReadAllHashes(hc.chainDb.BlockStore(), n)) > 0; n++ {
|
||||
nums = append([]uint64{n}, nums...) // suboptimal, but we don't really expect this path
|
||||
}
|
||||
origin = false
|
||||
@@ -697,23 +705,23 @@ func (hc *HeaderChain) setHead(headBlock uint64, headTime uint64, updateFn Updat
|
||||
// Remove the related data from the database on all sidechains
|
||||
for _, num := range nums {
|
||||
// Gather all the side fork hashes
|
||||
hashes := rawdb.ReadAllHashes(hc.chainDb, num)
|
||||
hashes := rawdb.ReadAllHashes(hc.chainDb.BlockStore(), num)
|
||||
if len(hashes) == 0 {
|
||||
// No hashes in the database whatsoever, probably frozen already
|
||||
hashes = append(hashes, hdr.Hash())
|
||||
}
|
||||
for _, hash := range hashes {
|
||||
if delFn != nil {
|
||||
delFn(batch, hash, num)
|
||||
delFn(blockBatch, hash, num)
|
||||
}
|
||||
rawdb.DeleteHeader(batch, hash, num)
|
||||
rawdb.DeleteTd(batch, hash, num)
|
||||
rawdb.DeleteHeader(blockBatch, hash, num)
|
||||
rawdb.DeleteTd(blockBatch, hash, num)
|
||||
}
|
||||
rawdb.DeleteCanonicalHash(batch, num)
|
||||
rawdb.DeleteCanonicalHash(blockBatch, num)
|
||||
}
|
||||
}
|
||||
// Flush all accumulated deletions.
|
||||
if err := batch.Write(); err != nil {
|
||||
if err := blockBatch.Write(); err != nil {
|
||||
log.Crit("Failed to rewind block", "error", err)
|
||||
}
|
||||
// Clear out any stale content from the caches
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/triedb"
|
||||
)
|
||||
|
||||
func verifyUnbrokenCanonchain(hc *HeaderChain) error {
|
||||
@@ -73,7 +73,7 @@ func TestHeaderInsertion(t *testing.T) {
|
||||
db = rawdb.NewMemoryDatabase()
|
||||
gspec = &Genesis{BaseFee: big.NewInt(params.InitialBaseFee), Config: params.AllEthashProtocolChanges}
|
||||
)
|
||||
gspec.Commit(db, trie.NewDatabase(db, nil))
|
||||
gspec.Commit(db, triedb.NewDatabase(db, nil))
|
||||
hc, err := NewHeaderChain(db, gspec.Config, ethash.NewFaker(), func() bool { return false })
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
|
||||
@@ -34,14 +34,23 @@ import (
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// Support Multi-Database Based on Data Pattern, the Chaindata will be divided into three stores: BlockStore, StateStore, and ChainStore,
|
||||
// according to data schema and read/write behavior. When using the following data interfaces, you should take note of the following:
|
||||
//
|
||||
// 1) Block-Related Data: For CanonicalHash, Header, Body, Td, Receipts, and BlobSidecars, the Write, Delete, and Iterator
|
||||
// operations should carefully ensure that the database being used is BlockStore.
|
||||
// 2) Meta-Related Data: For HeaderNumber, HeadHeaderHash, HeadBlockHash, HeadFastBlockHash, and FinalizedBlockHash, the
|
||||
// Write and Delete operations should carefully ensure that the database being used is BlockStore.
|
||||
// 3) Ancient Data: When using a multi-database, Ancient data will use the BlockStore.
|
||||
|
||||
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
|
||||
func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
data, _ = reader.Ancient(ChainFreezerHashTable, number)
|
||||
if len(data) == 0 {
|
||||
// Get it by hash from leveldb
|
||||
data, _ = db.Get(headerHashKey(number))
|
||||
data, _ = db.BlockStoreReader().Get(headerHashKey(number))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -144,8 +153,8 @@ func ReadAllCanonicalHashes(db ethdb.Iteratee, from uint64, to uint64, limit int
|
||||
}
|
||||
|
||||
// ReadHeaderNumber returns the header number assigned to a hash.
|
||||
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
|
||||
data, _ := db.Get(headerNumberKey(hash))
|
||||
func ReadHeaderNumber(db ethdb.MultiDatabaseReader, hash common.Hash) *uint64 {
|
||||
data, _ := db.BlockStoreReader().Get(headerNumberKey(hash))
|
||||
if len(data) != 8 {
|
||||
return nil
|
||||
}
|
||||
@@ -170,8 +179,8 @@ func DeleteHeaderNumber(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
}
|
||||
|
||||
// ReadHeadHeaderHash retrieves the hash of the current canonical head header.
|
||||
func ReadHeadHeaderHash(db ethdb.KeyValueReader) common.Hash {
|
||||
data, _ := db.Get(headHeaderKey)
|
||||
func ReadHeadHeaderHash(db ethdb.MultiDatabaseReader) common.Hash {
|
||||
data, _ := db.BlockStoreReader().Get(headHeaderKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -186,8 +195,8 @@ func WriteHeadHeaderHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
}
|
||||
|
||||
// ReadHeadBlockHash retrieves the hash of the current canonical head block.
|
||||
func ReadHeadBlockHash(db ethdb.KeyValueReader) common.Hash {
|
||||
data, _ := db.Get(headBlockKey)
|
||||
func ReadHeadBlockHash(db ethdb.MultiDatabaseReader) common.Hash {
|
||||
data, _ := db.BlockStoreReader().Get(headBlockKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -202,8 +211,8 @@ func WriteHeadBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
}
|
||||
|
||||
// ReadHeadFastBlockHash retrieves the hash of the current fast-sync head block.
|
||||
func ReadHeadFastBlockHash(db ethdb.KeyValueReader) common.Hash {
|
||||
data, _ := db.Get(headFastBlockKey)
|
||||
func ReadHeadFastBlockHash(db ethdb.MultiDatabaseReader) common.Hash {
|
||||
data, _ := db.BlockStoreReader().Get(headFastBlockKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
@@ -217,6 +226,22 @@ func WriteHeadFastBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
}
|
||||
}
|
||||
|
||||
// ReadFinalizedBlockHash retrieves the hash of the finalized block.
|
||||
func ReadFinalizedBlockHash(db ethdb.MultiDatabaseReader) common.Hash {
|
||||
data, _ := db.BlockStoreReader().Get(headFinalizedBlockKey)
|
||||
if len(data) == 0 {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(data)
|
||||
}
|
||||
|
||||
// WriteFinalizedBlockHash stores the hash of the finalized block.
|
||||
func WriteFinalizedBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||
if err := db.Put(headFinalizedBlockKey, hash.Bytes()); err != nil {
|
||||
log.Crit("Failed to store last finalized block's hash", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadLastPivotNumber retrieves the number of the last pivot block. If the node
|
||||
// full synced, the last pivot will always be nil.
|
||||
func ReadLastPivotNumber(db ethdb.KeyValueReader) *uint64 {
|
||||
@@ -281,13 +306,13 @@ func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValu
|
||||
// It's ok to request block 0, 1 item
|
||||
count = number + 1
|
||||
}
|
||||
limit, _ := db.Ancients()
|
||||
limit, _ := db.BlockStoreReader().Ancients()
|
||||
// First read live blocks
|
||||
if i >= limit {
|
||||
// If we need to read live blocks, we need to figure out the hash first
|
||||
hash := ReadCanonicalHash(db, number)
|
||||
for ; i >= limit && count > 0; i-- {
|
||||
if data, _ := db.Get(headerKey(i, hash)); len(data) > 0 {
|
||||
if data, _ := db.BlockStoreReader().Get(headerKey(i, hash)); len(data) > 0 {
|
||||
rlpHeaders = append(rlpHeaders, data)
|
||||
// Get the parent hash for next query
|
||||
hash = types.HeaderParentHashFromRLP(data)
|
||||
@@ -300,8 +325,8 @@ func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValu
|
||||
if count == 0 {
|
||||
return rlpHeaders
|
||||
}
|
||||
// read remaining from ancients
|
||||
data, err := db.AncientRange(ChainFreezerHeaderTable, i+1-count, count, 0)
|
||||
// read remaining from ancients, cap at 2M
|
||||
data, err := db.BlockStoreReader().AncientRange(ChainFreezerHeaderTable, i+1-count, count, 2*1024*1024)
|
||||
if err != nil {
|
||||
log.Error("Failed to read headers from freezer", "err", err)
|
||||
return rlpHeaders
|
||||
@@ -320,7 +345,7 @@ func ReadHeaderRange(db ethdb.Reader, number uint64, count uint64) []rlp.RawValu
|
||||
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
|
||||
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// First try to look up the data in ancient database. Extra hash
|
||||
// comparison is necessary since ancient database only maintains
|
||||
// the canonical data.
|
||||
@@ -329,7 +354,7 @@ func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValu
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(headerKey(number, hash))
|
||||
data, _ = db.BlockStoreReader().Get(headerKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -337,10 +362,10 @@ func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValu
|
||||
|
||||
// HasHeader verifies the existence of a block header corresponding to the hash.
|
||||
func HasHeader(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if isCanon(db, number, hash) {
|
||||
if isCanon(db.BlockStoreReader(), number, hash) {
|
||||
return true
|
||||
}
|
||||
if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
|
||||
if has, err := db.BlockStoreReader().Has(headerKey(number, hash)); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -360,6 +385,20 @@ func ReadHeader(db ethdb.Reader, hash common.Hash, number uint64) *types.Header
|
||||
return header
|
||||
}
|
||||
|
||||
// ReadHeaderAndRaw retrieves the block header corresponding to the hash.
|
||||
func ReadHeaderAndRaw(db ethdb.Reader, hash common.Hash, number uint64) (*types.Header, rlp.RawValue) {
|
||||
data := ReadHeaderRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
header := new(types.Header)
|
||||
if err := rlp.DecodeBytes(data, header); err != nil {
|
||||
log.Error("Invalid block header RLP", "hash", hash, "err", err)
|
||||
return nil, nil
|
||||
}
|
||||
return header, data
|
||||
}
|
||||
|
||||
// WriteHeader stores a block header into the database and also stores the hash-
|
||||
// to-number mapping.
|
||||
func WriteHeader(db ethdb.KeyValueWriter, header *types.Header) {
|
||||
@@ -413,14 +452,14 @@ func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue
|
||||
// comparison is necessary since ancient database only maintains
|
||||
// the canonical data.
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(ChainFreezerBodiesTable, number)
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(blockBodyKey(number, hash))
|
||||
data, _ = db.BlockStoreReader().Get(blockBodyKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -430,7 +469,7 @@ func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue
|
||||
// block at number, in RLP encoding.
|
||||
func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
data, _ = reader.Ancient(ChainFreezerBodiesTable, number)
|
||||
if len(data) > 0 {
|
||||
return nil
|
||||
@@ -438,8 +477,8 @@ func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
|
||||
// Block is not in ancients, read from leveldb by hash and number.
|
||||
// Note: ReadCanonicalHash cannot be used here because it also
|
||||
// calls ReadAncients internally.
|
||||
hash, _ := db.Get(headerHashKey(number))
|
||||
data, _ = db.Get(blockBodyKey(number, common.BytesToHash(hash)))
|
||||
hash, _ := db.BlockStoreReader().Get(headerHashKey(number))
|
||||
data, _ = db.BlockStoreReader().Get(blockBodyKey(number, common.BytesToHash(hash)))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -454,10 +493,10 @@ func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp
|
||||
|
||||
// HasBody verifies the existence of a block body corresponding to the hash.
|
||||
func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if isCanon(db, number, hash) {
|
||||
if isCanon(db.BlockStoreReader(), number, hash) {
|
||||
return true
|
||||
}
|
||||
if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
|
||||
if has, err := db.BlockStoreReader().Has(blockBodyKey(number, hash)); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -486,6 +525,13 @@ func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *t
|
||||
WriteBodyRLP(db, hash, number, data)
|
||||
}
|
||||
|
||||
// DeleteBody removes all block body data associated with a hash.
|
||||
func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(blockBodyKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete block body", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func WriteDiffLayer(db ethdb.KeyValueWriter, hash common.Hash, layer *types.DiffLayer) {
|
||||
data, err := rlp.EncodeToBytes(layer)
|
||||
if err != nil {
|
||||
@@ -524,24 +570,17 @@ func DeleteDiffLayer(db ethdb.KeyValueWriter, blockHash common.Hash) {
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteBody removes all block body data associated with a hash.
|
||||
func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(blockBodyKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete block body", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding.
|
||||
func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(ChainFreezerDifficultyTable, number)
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(headerTDKey(number, hash))
|
||||
data, _ = db.BlockStoreReader().Get(headerTDKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -582,10 +621,10 @@ func DeleteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
// HasReceipts verifies the existence of all the transaction receipts belonging
|
||||
// to a block.
|
||||
func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
if isCanon(db, number, hash) {
|
||||
if isCanon(db.BlockStoreReader(), number, hash) {
|
||||
return true
|
||||
}
|
||||
if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil {
|
||||
if has, err := db.BlockStoreReader().Has(blockReceiptsKey(number, hash)); !has || err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -594,14 +633,14 @@ func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
|
||||
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
|
||||
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(ChainFreezerReceiptTable, number)
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.Get(blockReceiptsKey(number, hash))
|
||||
data, _ = db.BlockStoreReader().Get(blockReceiptsKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
@@ -784,6 +823,48 @@ func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
|
||||
WriteHeader(db, block.Header())
|
||||
}
|
||||
|
||||
// WriteAncientBlocksWithBlobs writes entire block data with blobs into ancient store and returns the total written size.
|
||||
func WriteAncientBlocksWithBlobs(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, td *big.Int) (int64, error) {
|
||||
// find cancun index, it's used for new added blob ancient table
|
||||
cancunIndex := -1
|
||||
for i, block := range blocks {
|
||||
if block.Sidecars() != nil {
|
||||
cancunIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
log.Debug("WriteAncientBlocks", "startAt", blocks[0].Number(), "cancunIndex", cancunIndex, "len", len(blocks))
|
||||
|
||||
var (
|
||||
tdSum = new(big.Int).Set(td)
|
||||
preSize int64
|
||||
err error
|
||||
)
|
||||
if cancunIndex > 0 {
|
||||
preSize, err = WriteAncientBlocks(db, blocks[:cancunIndex], receipts[:cancunIndex], td)
|
||||
if err != nil {
|
||||
return preSize, err
|
||||
}
|
||||
for i, block := range blocks[:cancunIndex] {
|
||||
if i > 0 {
|
||||
tdSum.Add(tdSum, block.Difficulty())
|
||||
}
|
||||
}
|
||||
tdSum.Add(tdSum, blocks[cancunIndex].Difficulty())
|
||||
}
|
||||
|
||||
// It will reset blob ancient table at cancunIndex
|
||||
if cancunIndex >= 0 {
|
||||
if err = ResetEmptyBlobAncientTable(db, blocks[cancunIndex].NumberU64()); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
blocks = blocks[cancunIndex:]
|
||||
receipts = receipts[cancunIndex:]
|
||||
}
|
||||
postSize, err := WriteAncientBlocks(db, blocks, receipts, tdSum)
|
||||
return preSize + postSize, err
|
||||
}
|
||||
|
||||
// WriteAncientBlocks writes entire block data into ancient store and returns the total written size.
|
||||
func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, td *big.Int) (int64, error) {
|
||||
var (
|
||||
@@ -809,6 +890,56 @@ func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts
|
||||
})
|
||||
}
|
||||
|
||||
// ReadBlobSidecarsRLP retrieves all the transaction blobs belonging to a block in RLP encoding.
|
||||
func ReadBlobSidecarsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
|
||||
var data []byte
|
||||
db.BlockStoreReader().ReadAncients(func(reader ethdb.AncientReaderOp) error {
|
||||
// Check if the data is in ancients
|
||||
if isCanon(reader, number, hash) {
|
||||
data, _ = reader.Ancient(ChainFreezerBlobSidecarTable, number)
|
||||
return nil
|
||||
}
|
||||
// If not, try reading from leveldb
|
||||
data, _ = db.BlockStoreReader().Get(blockBlobSidecarsKey(number, hash))
|
||||
return nil
|
||||
})
|
||||
return data
|
||||
}
|
||||
|
||||
// ReadBlobSidecars retrieves all the transaction blobs belonging to a block.
|
||||
func ReadBlobSidecars(db ethdb.Reader, hash common.Hash, number uint64) types.BlobSidecars {
|
||||
data := ReadBlobSidecarsRLP(db, hash, number)
|
||||
if len(data) == 0 {
|
||||
return nil
|
||||
}
|
||||
var ret types.BlobSidecars
|
||||
if err := rlp.DecodeBytes(data, &ret); err != nil {
|
||||
log.Error("Invalid blob array RLP", "hash", hash, "err", err)
|
||||
return nil
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// WriteBlobSidecars stores all the transaction blobs belonging to a block.
|
||||
// It could input nil for empty blobs.
|
||||
func WriteBlobSidecars(db ethdb.KeyValueWriter, hash common.Hash, number uint64, blobs types.BlobSidecars) {
|
||||
data, err := rlp.EncodeToBytes(blobs)
|
||||
if err != nil {
|
||||
log.Crit("Failed to encode block blobs", "err", err)
|
||||
}
|
||||
// Store the flattened receipt slice
|
||||
if err := db.Put(blockBlobSidecarsKey(number, hash), data); err != nil {
|
||||
log.Crit("Failed to store block blobs", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// DeleteBlobSidecars removes all blob data associated with a block hash.
|
||||
func DeleteBlobSidecars(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
if err := db.Delete(blockBlobSidecarsKey(number, hash)); err != nil {
|
||||
log.Crit("Failed to delete block blobs", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage, td *big.Int) error {
|
||||
num := block.NumberU64()
|
||||
if err := op.AppendRaw(ChainFreezerHashTable, num, block.Hash().Bytes()); err != nil {
|
||||
@@ -826,6 +957,11 @@ func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *type
|
||||
if err := op.Append(ChainFreezerDifficultyTable, num, td); err != nil {
|
||||
return fmt.Errorf("can't append block %d total difficulty: %v", num, err)
|
||||
}
|
||||
if block.Sidecars() != nil {
|
||||
if err := op.Append(ChainFreezerBlobSidecarTable, num, block.Sidecars()); err != nil {
|
||||
return fmt.Errorf("can't append block %d blobs: %v", num, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -835,6 +971,7 @@ func DeleteBlock(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
|
||||
DeleteHeader(db, hash, number)
|
||||
DeleteBody(db, hash, number)
|
||||
DeleteTd(db, hash, number)
|
||||
DeleteBlobSidecars(db, hash, number) // it is safe to delete non-exist blob
|
||||
}
|
||||
|
||||
// DeleteBlockWithoutNumber removes all block data associated with a hash, except
|
||||
@@ -844,6 +981,7 @@ func DeleteBlockWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
|
||||
deleteHeaderWithoutNumber(db, hash, number)
|
||||
DeleteBody(db, hash, number)
|
||||
DeleteTd(db, hash, number)
|
||||
DeleteBlobSidecars(db, hash, number)
|
||||
}
|
||||
|
||||
const badBlockToKeep = 10
|
||||
|
||||
@@ -18,14 +18,21 @@ package rawdb
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
rand2 "crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/holiman/uint256"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -412,6 +419,62 @@ func TestBlockReceiptStorage(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestBlockBlobSidecarsStorage(t *testing.T) {
|
||||
db := NewMemoryDatabase()
|
||||
|
||||
// Create a live block since we need metadata to reconstruct the receipt
|
||||
genBlobs := makeBlkSidecars(1, 2)
|
||||
tx1 := types.NewTx(&types.BlobTx{
|
||||
ChainID: new(uint256.Int).SetUint64(1),
|
||||
GasTipCap: new(uint256.Int),
|
||||
GasFeeCap: new(uint256.Int),
|
||||
Gas: 0,
|
||||
Value: new(uint256.Int),
|
||||
Data: nil,
|
||||
BlobFeeCap: new(uint256.Int),
|
||||
BlobHashes: []common.Hash{common.HexToHash("0x34ec6e64f9cda8fe0451a391e4798085a3ef51a65ed1bfb016e34fc1a2028f8f"), common.HexToHash("0xb9a412e875f29fac436acde234f954e91173c4cf79814f6dcf630d8a6345747f")},
|
||||
Sidecar: genBlobs[0],
|
||||
V: new(uint256.Int),
|
||||
R: new(uint256.Int),
|
||||
S: new(uint256.Int),
|
||||
})
|
||||
tx2 := types.NewTx(&types.DynamicFeeTx{
|
||||
ChainID: new(big.Int).SetUint64(1),
|
||||
GasTipCap: new(big.Int),
|
||||
GasFeeCap: new(big.Int),
|
||||
Gas: 0,
|
||||
Value: new(big.Int),
|
||||
Data: nil,
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
})
|
||||
|
||||
blkHash := common.BytesToHash([]byte{0x03, 0x14})
|
||||
body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
|
||||
sidecars := types.BlobSidecars{types.NewBlobSidecarFromTx(tx1)}
|
||||
|
||||
// Check that no sidecars entries are in a pristine database
|
||||
if bs := ReadBlobSidecars(db, blkHash, 0); len(bs) != 0 {
|
||||
t.Fatalf("non existent sidecars returned: %v", bs)
|
||||
}
|
||||
WriteBody(db, blkHash, 0, body)
|
||||
WriteBlobSidecars(db, blkHash, 0, sidecars)
|
||||
|
||||
if bs := ReadBlobSidecars(db, blkHash, 0); len(bs) == 0 {
|
||||
t.Fatalf("no sidecars returned")
|
||||
} else {
|
||||
if err := checkBlobSidecarsRLP(bs, sidecars); err != nil {
|
||||
t.Fatalf(err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
DeleteBlobSidecars(db, blkHash, 0)
|
||||
if bs := ReadBlobSidecars(db, blkHash, 0); len(bs) != 0 {
|
||||
t.Fatalf("deleted sidecars returned: %v", bs)
|
||||
}
|
||||
}
|
||||
|
||||
func checkReceiptsRLP(have, want types.Receipts) error {
|
||||
if len(have) != len(want) {
|
||||
return fmt.Errorf("receipts sizes mismatch: have %d, want %d", len(have), len(want))
|
||||
@@ -432,10 +495,30 @@ func checkReceiptsRLP(have, want types.Receipts) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkBlobSidecarsRLP(have, want types.BlobSidecars) error {
|
||||
if len(have) != len(want) {
|
||||
return fmt.Errorf("blobs sizes mismatch: have %d, want %d", len(have), len(want))
|
||||
}
|
||||
for i := 0; i < len(want); i++ {
|
||||
rlpHave, err := rlp.EncodeToBytes(have[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rlpWant, err := rlp.EncodeToBytes(want[i])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(rlpHave, rlpWant) {
|
||||
return fmt.Errorf("blob #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAncientStorage(t *testing.T) {
|
||||
// Freezer style fast import the chain.
|
||||
frdir := t.TempDir()
|
||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false)
|
||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create database with ancient backend")
|
||||
}
|
||||
@@ -465,7 +548,8 @@ func TestAncientStorage(t *testing.T) {
|
||||
}
|
||||
|
||||
// Write and verify the header in the database
|
||||
WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil}, big.NewInt(100))
|
||||
_, err = WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil}, big.NewInt(100))
|
||||
require.NoError(t, err)
|
||||
|
||||
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
|
||||
t.Fatalf("no header returned")
|
||||
@@ -573,7 +657,7 @@ func TestHashesInRange(t *testing.T) {
|
||||
func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||
// Open freezer database.
|
||||
frdir := b.TempDir()
|
||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false)
|
||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create database with ancient backend")
|
||||
}
|
||||
@@ -586,6 +670,7 @@ func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||
const blockTxs = 20
|
||||
allBlocks := makeTestBlocks(b.N, blockTxs)
|
||||
batchReceipts := makeTestReceipts(batchSize, blockTxs)
|
||||
batchSidecars := makeTestSidecars(batchSize, blockTxs)
|
||||
b.ResetTimer()
|
||||
|
||||
// The benchmark loop writes batches of blocks, but note that the total block count is
|
||||
@@ -601,6 +686,9 @@ func BenchmarkWriteAncientBlocks(b *testing.B) {
|
||||
|
||||
blocks := allBlocks[i : i+length]
|
||||
receipts := batchReceipts[:length]
|
||||
for j := 0; j < length; j++ {
|
||||
blocks[i+j] = blocks[i+j].WithSidecars(batchSidecars[j])
|
||||
}
|
||||
writeSize, err := WriteAncientBlocks(db, blocks, receipts, td)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
@@ -663,6 +751,43 @@ func makeTestReceipts(n int, nPerBlock int) []types.Receipts {
|
||||
return allReceipts
|
||||
}
|
||||
|
||||
func makeBlkSidecars(n, nPerTx int) []*types.BlobTxSidecar {
|
||||
if n <= 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make([]*types.BlobTxSidecar, n)
|
||||
for i := 0; i < n; i++ {
|
||||
blobs := make([]kzg4844.Blob, nPerTx)
|
||||
commitments := make([]kzg4844.Commitment, nPerTx)
|
||||
proofs := make([]kzg4844.Proof, nPerTx)
|
||||
for i := 0; i < nPerTx; i++ {
|
||||
io.ReadFull(rand2.Reader, blobs[i][:])
|
||||
commitments[i], _ = kzg4844.BlobToCommitment(blobs[i])
|
||||
proofs[i], _ = kzg4844.ComputeBlobProof(blobs[i], commitments[i])
|
||||
}
|
||||
ret[i] = &types.BlobTxSidecar{
|
||||
Blobs: blobs,
|
||||
Commitments: commitments,
|
||||
Proofs: proofs,
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// makeTestSidecars creates fake blobs for the ancient write benchmark.
|
||||
func makeTestSidecars(n int, nPerBlock int) []types.BlobSidecars {
|
||||
allBlobs := make([]types.BlobSidecars, n)
|
||||
for i := 0; i < n; i++ {
|
||||
raws := makeBlkSidecars(nPerBlock, i%3)
|
||||
var sidecars types.BlobSidecars
|
||||
for _, s := range raws {
|
||||
sidecars = append(sidecars, &types.BlobSidecar{BlobTxSidecar: *s})
|
||||
}
|
||||
allBlobs[i] = sidecars
|
||||
}
|
||||
return allBlobs
|
||||
}
|
||||
|
||||
type fullLogRLP struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
@@ -876,7 +1001,7 @@ func TestHeadersRLPStorage(t *testing.T) {
|
||||
// Have N headers in the freezer
|
||||
frdir := t.TempDir()
|
||||
|
||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false)
|
||||
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false, false, false, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create database with ancient backend")
|
||||
}
|
||||
|
||||
@@ -288,13 +288,13 @@ func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, has
|
||||
// if the state is not present in database.
|
||||
func ReadStateScheme(db ethdb.Reader) string {
|
||||
// Check if state in path-based scheme is present
|
||||
blob, _ := ReadAccountTrieNode(db, nil)
|
||||
blob, _ := ReadAccountTrieNode(db.StateStoreReader(), nil)
|
||||
if len(blob) != 0 {
|
||||
return PathScheme
|
||||
}
|
||||
// The root node might be deleted during the initial snap sync, check
|
||||
// the persistent state id then.
|
||||
if id := ReadPersistentStateID(db); id != 0 {
|
||||
if id := ReadPersistentStateID(db.StateStoreReader()); id != 0 {
|
||||
return PathScheme
|
||||
}
|
||||
// In a hash-based scheme, the genesis state is consistently stored
|
||||
@@ -304,7 +304,7 @@ func ReadStateScheme(db ethdb.Reader) string {
|
||||
if header == nil {
|
||||
return "" // empty datadir
|
||||
}
|
||||
blob = ReadLegacyTrieNode(db, header.Root)
|
||||
blob = ReadLegacyTrieNode(db.StateStoreReader(), header.Root)
|
||||
if len(blob) == 0 {
|
||||
return "" // no state in disk
|
||||
}
|
||||
@@ -324,7 +324,7 @@ func ValidateStateScheme(stateScheme string) bool {
|
||||
// the stored state.
|
||||
//
|
||||
// - If the provided scheme is none, use the scheme consistent with persistent
|
||||
// state, or fallback to hash-based scheme if state is empty.
|
||||
// state, or fallback to path-based scheme if state is empty.
|
||||
//
|
||||
// - If the provided scheme is hash, use hash-based scheme or error out if not
|
||||
// compatible with persistent state scheme.
|
||||
@@ -338,10 +338,8 @@ func ParseStateScheme(provided string, disk ethdb.Database) (string, error) {
|
||||
stored := ReadStateScheme(disk)
|
||||
if provided == "" {
|
||||
if stored == "" {
|
||||
// use default scheme for empty database, flip it when
|
||||
// path mode is chosen as default
|
||||
log.Info("State scheme set to default", "scheme", "hash")
|
||||
return HashScheme, nil
|
||||
log.Info("State scheme set to default", "scheme", "path")
|
||||
return PathScheme, nil // use default scheme for empty database
|
||||
}
|
||||
log.Info("State scheme set to already existing disk db", "scheme", stored)
|
||||
return stored, nil // reuse scheme of persistent scheme
|
||||
|
||||
@@ -34,18 +34,24 @@ const (
|
||||
|
||||
// ChainFreezerDifficultyTable indicates the name of the freezer total difficulty table.
|
||||
ChainFreezerDifficultyTable = "diffs"
|
||||
|
||||
// ChainFreezerBlobSidecarTable indicates the name of the freezer total blob table.
|
||||
ChainFreezerBlobSidecarTable = "blobs"
|
||||
)
|
||||
|
||||
// chainFreezerNoSnappy configures whether compression is disabled for the ancient-tables.
|
||||
// Hashes and difficulties don't compress well.
|
||||
var chainFreezerNoSnappy = map[string]bool{
|
||||
ChainFreezerHeaderTable: false,
|
||||
ChainFreezerHashTable: true,
|
||||
ChainFreezerBodiesTable: false,
|
||||
ChainFreezerReceiptTable: false,
|
||||
ChainFreezerDifficultyTable: true,
|
||||
ChainFreezerHeaderTable: false,
|
||||
ChainFreezerHashTable: true,
|
||||
ChainFreezerBodiesTable: false,
|
||||
ChainFreezerReceiptTable: false,
|
||||
ChainFreezerDifficultyTable: true,
|
||||
ChainFreezerBlobSidecarTable: false,
|
||||
}
|
||||
|
||||
var additionTables = []string{ChainFreezerBlobSidecarTable}
|
||||
|
||||
const (
|
||||
// stateHistoryTableSize defines the maximum size of freezer data files.
|
||||
stateHistoryTableSize = 2 * 1000 * 1000 * 1000
|
||||
|
||||
@@ -18,6 +18,8 @@ package rawdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -91,13 +93,25 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
|
||||
infos = append(infos, info)
|
||||
|
||||
case StateFreezerName:
|
||||
if ReadStateScheme(db) != PathScheme {
|
||||
if ReadStateScheme(db) != PathScheme || db.StateStore() != nil {
|
||||
continue
|
||||
}
|
||||
datadir, err := db.AncientDatadir()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Join(datadir, StateFreezerName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
// if state freezer folder has been pruned, there is no need for inspection
|
||||
_, err = file.Readdirnames(1)
|
||||
if err == io.EOF {
|
||||
continue
|
||||
}
|
||||
|
||||
f, err := NewStateFreezer(datadir, true, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -121,16 +135,25 @@ func inspectFreezers(db ethdb.Database) ([]freezerInfo, error) {
|
||||
// ancient indicates the path of root ancient directory where the chain freezer can
|
||||
// be opened. Start and end specify the range for dumping out indexes.
|
||||
// Note this function can only be used for debugging purposes.
|
||||
func InspectFreezerTable(ancient string, freezerName string, tableName string, start, end int64) error {
|
||||
func InspectFreezerTable(ancient string, freezerName string, tableName string, start, end int64, multiDatabase bool) error {
|
||||
var (
|
||||
path string
|
||||
tables map[string]bool
|
||||
)
|
||||
switch freezerName {
|
||||
case ChainFreezerName:
|
||||
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
|
||||
if multiDatabase {
|
||||
path, tables = resolveChainFreezerDir(filepath.Dir(ancient)+"/block/ancient"), chainFreezerNoSnappy
|
||||
} else {
|
||||
path, tables = resolveChainFreezerDir(ancient), chainFreezerNoSnappy
|
||||
}
|
||||
|
||||
case StateFreezerName:
|
||||
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
|
||||
if multiDatabase {
|
||||
path, tables = filepath.Join(filepath.Dir(ancient)+"/state/ancient", freezerName), stateFreezerNoSnappy
|
||||
} else {
|
||||
path, tables = filepath.Join(ancient, freezerName), stateFreezerNoSnappy
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unknown freezer, supported ones: %v", freezers)
|
||||
}
|
||||
|
||||
@@ -17,15 +17,19 @@
|
||||
package rawdb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -39,6 +43,10 @@ const (
|
||||
freezerBatchLimit = 30000
|
||||
)
|
||||
|
||||
var (
|
||||
missFreezerEnvErr = errors.New("missing freezer env error")
|
||||
)
|
||||
|
||||
// chainFreezer is a wrapper of freezer with additional chain freezing feature.
|
||||
// The background thread will keep moving ancient chain segments from key-value
|
||||
// database to flat files for saving space on live database.
|
||||
@@ -49,10 +57,15 @@ type chainFreezer struct {
|
||||
quit chan struct{}
|
||||
wg sync.WaitGroup
|
||||
trigger chan chan struct{} // Manual blocking freeze trigger, test determinism
|
||||
|
||||
freezeEnv atomic.Value
|
||||
waitEnvTimes int
|
||||
|
||||
multiDatabase bool
|
||||
}
|
||||
|
||||
// newChainFreezer initializes the freezer for ancient chain data.
|
||||
func newChainFreezer(datadir string, namespace string, readonly bool, offset uint64) (*chainFreezer, error) {
|
||||
func newChainFreezer(datadir string, namespace string, readonly bool, offset uint64, multiDatabase bool) (*chainFreezer, error) {
|
||||
freezer, err := NewChainFreezer(datadir, namespace, readonly, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -77,6 +90,57 @@ func (f *chainFreezer) Close() error {
|
||||
return f.Freezer.Close()
|
||||
}
|
||||
|
||||
// readHeadNumber returns the number of chain head block. 0 is returned if the
|
||||
// block is unknown or not available yet.
|
||||
func (f *chainFreezer) readHeadNumber(db ethdb.Reader) uint64 {
|
||||
hash := ReadHeadBlockHash(db)
|
||||
if hash == (common.Hash{}) {
|
||||
log.Error("Head block is not reachable")
|
||||
return 0
|
||||
}
|
||||
number := ReadHeaderNumber(db, hash)
|
||||
if number == nil {
|
||||
log.Error("Number of head block is missing")
|
||||
return 0
|
||||
}
|
||||
return *number
|
||||
}
|
||||
|
||||
// readFinalizedNumber returns the number of finalized block. 0 is returned
|
||||
// if the block is unknown or not available yet.
|
||||
func (f *chainFreezer) readFinalizedNumber(db ethdb.Reader) uint64 {
|
||||
hash := ReadFinalizedBlockHash(db)
|
||||
if hash == (common.Hash{}) {
|
||||
return 0
|
||||
}
|
||||
number := ReadHeaderNumber(db, hash)
|
||||
if number == nil {
|
||||
log.Error("Number of finalized block is missing")
|
||||
return 0
|
||||
}
|
||||
return *number
|
||||
}
|
||||
|
||||
// freezeThreshold returns the threshold for chain freezing. It's determined
|
||||
// by formula: max(finality, HEAD-params.FullImmutabilityThreshold).
|
||||
func (f *chainFreezer) freezeThreshold(db ethdb.Reader) (uint64, error) {
|
||||
var (
|
||||
head = f.readHeadNumber(db)
|
||||
final = f.readFinalizedNumber(db)
|
||||
headLimit uint64
|
||||
)
|
||||
if head > params.FullImmutabilityThreshold {
|
||||
headLimit = head - params.FullImmutabilityThreshold
|
||||
}
|
||||
if final == 0 && headLimit == 0 {
|
||||
return 0, errors.New("freezing threshold is not available")
|
||||
}
|
||||
if final > headLimit {
|
||||
return final, nil
|
||||
}
|
||||
return headLimit, nil
|
||||
}
|
||||
|
||||
// freeze is a background thread that periodically checks the blockchain for any
|
||||
// import progress and moves ancient data from the fast database into the freezer.
|
||||
//
|
||||
@@ -114,60 +178,126 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
||||
return
|
||||
}
|
||||
}
|
||||
// Retrieve the freezing threshold.
|
||||
hash := ReadHeadBlockHash(nfdb)
|
||||
if hash == (common.Hash{}) {
|
||||
log.Debug("Current full block hash unavailable") // new chain, empty database
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
number := ReadHeaderNumber(nfdb, hash)
|
||||
threshold := f.threshold.Load()
|
||||
frozen := f.frozen.Load()
|
||||
switch {
|
||||
case number == nil:
|
||||
log.Error("Current full block number unavailable", "hash", hash)
|
||||
backoff = true
|
||||
continue
|
||||
|
||||
case *number < threshold:
|
||||
log.Debug("Current full block not old enough to freeze", "number", *number, "hash", hash, "delay", threshold)
|
||||
backoff = true
|
||||
continue
|
||||
var (
|
||||
frozen uint64
|
||||
threshold uint64
|
||||
first uint64 // the first block to freeze
|
||||
last uint64 // the last block to freeze
|
||||
|
||||
case *number-threshold <= frozen:
|
||||
log.Debug("Ancient blocks frozen already", "number", *number, "hash", hash, "frozen", frozen)
|
||||
backoff = true
|
||||
continue
|
||||
hash common.Hash
|
||||
number *uint64
|
||||
head *types.Header
|
||||
err error
|
||||
)
|
||||
|
||||
// use finalized block as the chain freeze indicator was used for multiDatabase feature, if multiDatabase is false, keep 9W blocks in db
|
||||
if f.multiDatabase {
|
||||
threshold, err = f.freezeThreshold(nfdb)
|
||||
if err != nil {
|
||||
backoff = true
|
||||
log.Debug("Current full block not old enough to freeze", "err", err)
|
||||
continue
|
||||
}
|
||||
frozen = f.frozen.Load()
|
||||
|
||||
// Short circuit if the blocks below threshold are already frozen.
|
||||
if frozen != 0 && frozen-1 >= threshold {
|
||||
backoff = true
|
||||
log.Debug("Ancient blocks frozen already", "threshold", threshold, "frozen", frozen)
|
||||
continue
|
||||
}
|
||||
|
||||
hash = ReadHeadBlockHash(nfdb)
|
||||
if hash == (common.Hash{}) {
|
||||
log.Debug("Current full block hash unavailable") // new chain, empty database
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
number = ReadHeaderNumber(nfdb, hash)
|
||||
if number == nil {
|
||||
log.Error("Current full block number unavailable", "hash", hash)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
head = ReadHeader(nfdb, hash, *number)
|
||||
if head == nil {
|
||||
log.Error("Current full block unavailable", "number", *number, "hash", hash)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
|
||||
first = frozen
|
||||
last = threshold
|
||||
if last-first+1 > freezerBatchLimit {
|
||||
last = freezerBatchLimit + first - 1
|
||||
}
|
||||
} else {
|
||||
// Retrieve the freezing threshold.
|
||||
hash = ReadHeadBlockHash(nfdb)
|
||||
if hash == (common.Hash{}) {
|
||||
log.Debug("Current full block hash unavailable") // new chain, empty database
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
number = ReadHeaderNumber(nfdb, hash)
|
||||
threshold = f.threshold.Load()
|
||||
frozen = f.frozen.Load()
|
||||
switch {
|
||||
case number == nil:
|
||||
log.Error("Current full block number unavailable", "hash", hash)
|
||||
backoff = true
|
||||
continue
|
||||
|
||||
case *number < threshold:
|
||||
log.Debug("Current full block not old enough to freeze", "number", *number, "hash", hash, "delay", threshold)
|
||||
backoff = true
|
||||
continue
|
||||
|
||||
case *number-threshold <= frozen:
|
||||
log.Debug("Ancient blocks frozen already", "number", *number, "hash", hash, "frozen", frozen)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
head = ReadHeader(nfdb, hash, *number)
|
||||
if head == nil {
|
||||
log.Error("Current full block unavailable", "number", *number, "hash", hash)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
first, _ = f.Ancients()
|
||||
last = *number - threshold
|
||||
if last-first > freezerBatchLimit {
|
||||
last = first + freezerBatchLimit
|
||||
}
|
||||
}
|
||||
head := ReadHeader(nfdb, hash, *number)
|
||||
if head == nil {
|
||||
log.Error("Current full block unavailable", "number", *number, "hash", hash)
|
||||
|
||||
// check env first before chain freeze, it must wait when the env is necessary
|
||||
if err := f.checkFreezerEnv(); err != nil {
|
||||
f.waitEnvTimes++
|
||||
if f.waitEnvTimes%30 == 0 {
|
||||
log.Warn("Freezer need related env, may wait for a while, and it's not a issue when non-import block", "err", err)
|
||||
return
|
||||
}
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Seems we have data ready to be frozen, process in usable batches
|
||||
var (
|
||||
start = time.Now()
|
||||
first, _ = f.Ancients()
|
||||
limit = *number - threshold
|
||||
start = time.Now()
|
||||
)
|
||||
if limit-first > freezerBatchLimit {
|
||||
limit = first + freezerBatchLimit
|
||||
}
|
||||
ancients, err := f.freezeRange(nfdb, first, limit)
|
||||
|
||||
ancients, err := f.freezeRangeWithBlobs(nfdb, first, last)
|
||||
if err != nil {
|
||||
log.Error("Error in block freeze operation", "err", err)
|
||||
backoff = true
|
||||
continue
|
||||
}
|
||||
|
||||
// Batch of blocks have been frozen, flush them before wiping from leveldb
|
||||
if err := f.Sync(); err != nil {
|
||||
log.Crit("Failed to flush frozen tables", "err", err)
|
||||
}
|
||||
|
||||
// Wipe out all data from the active database
|
||||
batch := db.NewBatch()
|
||||
for i := 0; i < len(ancients); i++ {
|
||||
@@ -243,6 +373,12 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
||||
}
|
||||
log.Debug("Deep froze chain segment", context...)
|
||||
|
||||
env, _ := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
||||
// try prune blob data after cancun fork
|
||||
if isCancun(env, head.Number, head.Time) {
|
||||
f.tryPruneBlobAncientTable(env, *number)
|
||||
}
|
||||
|
||||
// Avoid database thrashing with tiny writes
|
||||
if frozen-first < freezerBatchLimit {
|
||||
backoff = true
|
||||
@@ -250,9 +386,93 @@ func (f *chainFreezer) freeze(db ethdb.KeyValueStore) {
|
||||
}
|
||||
}
|
||||
|
||||
func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) {
|
||||
hashes = make([]common.Hash, 0, limit-number)
|
||||
func (f *chainFreezer) tryPruneBlobAncientTable(env *ethdb.FreezerEnv, num uint64) {
|
||||
extraReserve := getBlobExtraReserveFromEnv(env)
|
||||
// It means that there is no need for pruning
|
||||
if extraReserve == 0 {
|
||||
return
|
||||
}
|
||||
reserveThreshold := params.MinBlocksForBlobRequests + extraReserve
|
||||
if num <= reserveThreshold {
|
||||
return
|
||||
}
|
||||
expectTail := num - reserveThreshold
|
||||
start := time.Now()
|
||||
if _, err := f.TruncateTableTail(ChainFreezerBlobSidecarTable, expectTail); err != nil {
|
||||
log.Error("Cannot prune blob ancient", "block", num, "expectTail", expectTail, "err", err)
|
||||
return
|
||||
}
|
||||
log.Debug("Chain freezer prune useless blobs, now ancient data is", "from", expectTail, "to", num, "cost", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
|
||||
func getBlobExtraReserveFromEnv(env *ethdb.FreezerEnv) uint64 {
|
||||
if env == nil {
|
||||
return params.DefaultExtraReserveForBlobRequests
|
||||
}
|
||||
return env.BlobExtraReserve
|
||||
}
|
||||
|
||||
func (f *chainFreezer) freezeRangeWithBlobs(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) {
|
||||
defer func() {
|
||||
log.Debug("freezeRangeWithBlobs", "from", number, "to", limit, "err", err)
|
||||
}()
|
||||
lastHash := ReadCanonicalHash(nfdb, limit)
|
||||
if lastHash == (common.Hash{}) {
|
||||
return nil, fmt.Errorf("canonical hash missing, can't freeze block %d", limit)
|
||||
}
|
||||
last, _ := ReadHeaderAndRaw(nfdb, lastHash, limit)
|
||||
if last == nil {
|
||||
return nil, fmt.Errorf("block header missing, can't freeze block %d", limit)
|
||||
}
|
||||
env, _ := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
||||
if !isCancun(env, last.Number, last.Time) {
|
||||
return f.freezeRange(nfdb, number, limit)
|
||||
}
|
||||
|
||||
var (
|
||||
cancunNumber uint64
|
||||
preHashes []common.Hash
|
||||
)
|
||||
for i := number; i <= limit; i++ {
|
||||
hash := ReadCanonicalHash(nfdb, i)
|
||||
if hash == (common.Hash{}) {
|
||||
return nil, fmt.Errorf("canonical hash missing, can't freeze block %d", i)
|
||||
}
|
||||
h, header := ReadHeaderAndRaw(nfdb, hash, i)
|
||||
if len(header) == 0 {
|
||||
return nil, fmt.Errorf("block header missing, can't freeze block %d", i)
|
||||
}
|
||||
if isCancun(env, h.Number, h.Time) {
|
||||
cancunNumber = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// freeze pre cancun
|
||||
preHashes, err = f.freezeRange(nfdb, number, cancunNumber-1)
|
||||
if err != nil {
|
||||
return preHashes, err
|
||||
}
|
||||
|
||||
if err = ResetEmptyBlobAncientTable(f, cancunNumber); err != nil {
|
||||
return preHashes, err
|
||||
}
|
||||
// freeze post cancun
|
||||
postHashes, err := f.freezeRange(nfdb, cancunNumber, limit)
|
||||
hashes = append(preHashes, postHashes...)
|
||||
return hashes, err
|
||||
}
|
||||
|
||||
// freezeRange moves a batch of chain segments from the fast database to the freezer.
|
||||
// The parameters (number, limit) specify the relevant block range, both of which
|
||||
// are included.
|
||||
func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) {
|
||||
if number > limit {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
env, _ := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
||||
hashes = make([]common.Hash, 0, limit-number+1)
|
||||
_, err = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
|
||||
for ; number <= limit; number++ {
|
||||
// Retrieve all the components of the canonical block.
|
||||
@@ -260,7 +480,7 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||
if hash == (common.Hash{}) {
|
||||
return fmt.Errorf("canonical hash missing, can't freeze block %d", number)
|
||||
}
|
||||
header := ReadHeaderRLP(nfdb, hash, number)
|
||||
h, header := ReadHeaderAndRaw(nfdb, hash, number)
|
||||
if len(header) == 0 {
|
||||
return fmt.Errorf("block header missing, can't freeze block %d", number)
|
||||
}
|
||||
@@ -276,6 +496,14 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||
if len(td) == 0 {
|
||||
return fmt.Errorf("total difficulty missing, can't freeze block %d", number)
|
||||
}
|
||||
// blobs is nil before cancun fork
|
||||
var sidecars rlp.RawValue
|
||||
if isCancun(env, h.Number, h.Time) {
|
||||
sidecars = ReadBlobSidecarsRLP(nfdb, hash, number)
|
||||
if len(sidecars) == 0 {
|
||||
return fmt.Errorf("block blobs missing, can't freeze block %d", number)
|
||||
}
|
||||
}
|
||||
|
||||
// Write to the batch.
|
||||
if err := op.AppendRaw(ChainFreezerHashTable, number, hash[:]); err != nil {
|
||||
@@ -293,6 +521,11 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||
if err := op.AppendRaw(ChainFreezerDifficultyTable, number, td); err != nil {
|
||||
return fmt.Errorf("can't write td to Freezer: %v", err)
|
||||
}
|
||||
if isCancun(env, h.Number, h.Time) {
|
||||
if err := op.AppendRaw(ChainFreezerBlobSidecarTable, number, sidecars); err != nil {
|
||||
return fmt.Errorf("can't write blobs to Freezer: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
hashes = append(hashes, hash)
|
||||
}
|
||||
@@ -301,3 +534,28 @@ func (f *chainFreezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hash
|
||||
|
||||
return hashes, err
|
||||
}
|
||||
|
||||
func (f *chainFreezer) SetupFreezerEnv(env *ethdb.FreezerEnv) error {
|
||||
f.freezeEnv.Store(env)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *chainFreezer) checkFreezerEnv() error {
|
||||
_, exist := f.freezeEnv.Load().(*ethdb.FreezerEnv)
|
||||
if exist {
|
||||
return nil
|
||||
}
|
||||
return missFreezerEnvErr
|
||||
}
|
||||
|
||||
func isCancun(env *ethdb.FreezerEnv, num *big.Int, time uint64) bool {
|
||||
if env == nil || env.ChainCfg == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return env.ChainCfg.IsCancun(num, time)
|
||||
}
|
||||
|
||||
func ResetEmptyBlobAncientTable(db ethdb.AncientWriter, next uint64) error {
|
||||
return db.ResetTable(ChainFreezerBlobSidecarTable, next, true)
|
||||
}
|
||||
|
||||
@@ -35,16 +35,16 @@ import (
|
||||
// injects into the database the block hash->number mappings.
|
||||
func InitDatabaseFromFreezer(db ethdb.Database) {
|
||||
// If we can't access the freezer or it's empty, abort
|
||||
frozen, err := db.ItemAmountInAncient()
|
||||
frozen, err := db.BlockStore().ItemAmountInAncient()
|
||||
if err != nil || frozen == 0 {
|
||||
return
|
||||
}
|
||||
var (
|
||||
batch = db.NewBatch()
|
||||
batch = db.BlockStore().NewBatch()
|
||||
start = time.Now()
|
||||
logged = start.Add(-7 * time.Second) // Unindex during import is fast, don't double log
|
||||
hash common.Hash
|
||||
offset = db.AncientOffSet()
|
||||
offset = db.BlockStore().AncientOffSet()
|
||||
)
|
||||
for i := uint64(0) + offset; i < frozen+offset; i++ {
|
||||
// We read 100K hashes at a time, for a total of 3.2M
|
||||
@@ -52,7 +52,7 @@ func InitDatabaseFromFreezer(db ethdb.Database) {
|
||||
if i+count > frozen+offset {
|
||||
count = frozen + offset - i
|
||||
}
|
||||
data, err := db.AncientRange(ChainFreezerHashTable, i, count, 32*count)
|
||||
data, err := db.BlockStore().AncientRange(ChainFreezerHashTable, i, count, 32*count)
|
||||
if err != nil {
|
||||
log.Crit("Failed to init database from freezer", "err", err)
|
||||
}
|
||||
@@ -80,8 +80,8 @@ func InitDatabaseFromFreezer(db ethdb.Database) {
|
||||
}
|
||||
batch.Reset()
|
||||
|
||||
WriteHeadHeaderHash(db, hash)
|
||||
WriteHeadFastBlockHash(db, hash)
|
||||
WriteHeadHeaderHash(db.BlockStore(), hash)
|
||||
WriteHeadFastBlockHash(db.BlockStore(), hash)
|
||||
log.Info("Initialized database from freezer", "blocks", frozen, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
|
||||
@@ -100,7 +100,7 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
|
||||
number uint64
|
||||
rlp rlp.RawValue
|
||||
}
|
||||
if offset := db.AncientOffSet(); offset > from {
|
||||
if offset := db.BlockStore().AncientOffSet(); offset > from {
|
||||
from = offset
|
||||
}
|
||||
if to <= from {
|
||||
@@ -187,7 +187,7 @@ func iterateTransactions(db ethdb.Database, from uint64, to uint64, reverse bool
|
||||
// signal received.
|
||||
func indexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
|
||||
// short circuit for invalid range
|
||||
if offset := db.AncientOffSet(); offset > from {
|
||||
if offset := db.BlockStore().AncientOffSet(); offset > from {
|
||||
from = offset
|
||||
}
|
||||
if from >= to {
|
||||
@@ -286,7 +286,7 @@ func indexTransactionsForTesting(db ethdb.Database, from uint64, to uint64, inte
|
||||
// signal received.
|
||||
func unindexTransactions(db ethdb.Database, from uint64, to uint64, interrupt chan struct{}, hook func(uint64) bool, report bool) {
|
||||
// short circuit for invalid range
|
||||
if offset := db.AncientOffSet(); offset > from {
|
||||
if offset := db.BlockStore().AncientOffSet(); offset > from {
|
||||
from = offset
|
||||
}
|
||||
if from >= to {
|
||||
|
||||
@@ -26,14 +26,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/olekukonko/tablewriter"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/leveldb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||
"github.com/ethereum/go-ethereum/ethdb/pebble"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
"github.com/olekukonko/tablewriter"
|
||||
)
|
||||
|
||||
// freezerdb is a database wrapper that enables freezer data retrievals.
|
||||
@@ -41,7 +40,31 @@ type freezerdb struct {
|
||||
ancientRoot string
|
||||
ethdb.KeyValueStore
|
||||
ethdb.AncientStore
|
||||
diffStore ethdb.KeyValueStore
|
||||
ethdb.AncientFreezer
|
||||
diffStore ethdb.KeyValueStore
|
||||
stateStore ethdb.Database
|
||||
blockStore ethdb.Database
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) StateStoreReader() ethdb.Reader {
|
||||
if frdb.stateStore == nil {
|
||||
return frdb
|
||||
}
|
||||
return frdb.stateStore
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) BlockStoreReader() ethdb.Reader {
|
||||
if frdb.blockStore == nil {
|
||||
return frdb
|
||||
}
|
||||
return frdb.blockStore
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) BlockStoreWriter() ethdb.Writer {
|
||||
if frdb.blockStore == nil {
|
||||
return frdb
|
||||
}
|
||||
return frdb.blockStore
|
||||
}
|
||||
|
||||
// AncientDatadir returns the path of root ancient directory.
|
||||
@@ -64,6 +87,16 @@ func (frdb *freezerdb) Close() error {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if frdb.stateStore != nil {
|
||||
if err := frdb.stateStore.Close(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if frdb.blockStore != nil {
|
||||
if err := frdb.blockStore.Close(); err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
if len(errs) != 0 {
|
||||
return fmt.Errorf("%v", errs)
|
||||
}
|
||||
@@ -81,6 +114,43 @@ func (frdb *freezerdb) SetDiffStore(diff ethdb.KeyValueStore) {
|
||||
frdb.diffStore = diff
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) StateStore() ethdb.Database {
|
||||
return frdb.stateStore
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) GetStateStore() ethdb.Database {
|
||||
if frdb.stateStore != nil {
|
||||
return frdb.stateStore
|
||||
}
|
||||
return frdb
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) SetStateStore(state ethdb.Database) {
|
||||
if frdb.stateStore != nil {
|
||||
frdb.stateStore.Close()
|
||||
}
|
||||
frdb.stateStore = state
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) BlockStore() ethdb.Database {
|
||||
if frdb.blockStore != nil {
|
||||
return frdb.blockStore
|
||||
} else {
|
||||
return frdb
|
||||
}
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) SetBlockStore(block ethdb.Database) {
|
||||
if frdb.blockStore != nil {
|
||||
frdb.blockStore.Close()
|
||||
}
|
||||
frdb.blockStore = block
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) HasSeparateBlockStore() bool {
|
||||
return frdb.blockStore != nil
|
||||
}
|
||||
|
||||
// Freeze is a helper method used for external testing to trigger and block until
|
||||
// a freeze cycle completes, without having to sleep for a minute to trigger the
|
||||
// automatic background run.
|
||||
@@ -93,7 +163,6 @@ func (frdb *freezerdb) Freeze(threshold uint64) error {
|
||||
frdb.AncientStore.(*chainFreezer).threshold.Store(old)
|
||||
}(frdb.AncientStore.(*chainFreezer).threshold.Load())
|
||||
frdb.AncientStore.(*chainFreezer).threshold.Store(threshold)
|
||||
|
||||
// Trigger a freeze cycle and block until it's done
|
||||
trigger := make(chan struct{}, 1)
|
||||
frdb.AncientStore.(*chainFreezer).trigger <- trigger
|
||||
@@ -101,10 +170,16 @@ func (frdb *freezerdb) Freeze(threshold uint64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (frdb *freezerdb) SetupFreezerEnv(env *ethdb.FreezerEnv) error {
|
||||
return frdb.AncientFreezer.SetupFreezerEnv(env)
|
||||
}
|
||||
|
||||
// nofreezedb is a database wrapper that disables freezer data retrievals.
|
||||
type nofreezedb struct {
|
||||
ethdb.KeyValueStore
|
||||
diffStore ethdb.KeyValueStore
|
||||
diffStore ethdb.KeyValueStore
|
||||
stateStore ethdb.Database
|
||||
blockStore ethdb.Database
|
||||
}
|
||||
|
||||
// HasAncient returns an error as we don't have a backing chain freezer.
|
||||
@@ -127,7 +202,7 @@ func (db *nofreezedb) Ancients() (uint64, error) {
|
||||
return 0, errNotSupported
|
||||
}
|
||||
|
||||
// Ancients returns an error as we don't have a backing chain freezer.
|
||||
// ItemAmountInAncient returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) ItemAmountInAncient() (uint64, error) {
|
||||
return 0, errNotSupported
|
||||
}
|
||||
@@ -157,6 +232,16 @@ func (db *nofreezedb) TruncateTail(items uint64) (uint64, error) {
|
||||
return 0, errNotSupported
|
||||
}
|
||||
|
||||
// TruncateTableTail will truncate certain table to new tail
|
||||
func (db *nofreezedb) TruncateTableTail(kind string, tail uint64) (uint64, error) {
|
||||
return 0, errNotSupported
|
||||
}
|
||||
|
||||
// ResetTable will reset certain table with new start point
|
||||
func (db *nofreezedb) ResetTable(kind string, startAt uint64, onlyEmpty bool) error {
|
||||
return errNotSupported
|
||||
}
|
||||
|
||||
// Sync returns an error as we don't have a backing chain freezer.
|
||||
func (db *nofreezedb) Sync() error {
|
||||
return errNotSupported
|
||||
@@ -170,6 +255,57 @@ func (db *nofreezedb) SetDiffStore(diff ethdb.KeyValueStore) {
|
||||
db.diffStore = diff
|
||||
}
|
||||
|
||||
func (db *nofreezedb) StateStore() ethdb.Database {
|
||||
return db.stateStore
|
||||
}
|
||||
|
||||
func (db *nofreezedb) SetStateStore(state ethdb.Database) {
|
||||
db.stateStore = state
|
||||
}
|
||||
|
||||
func (db *nofreezedb) GetStateStore() ethdb.Database {
|
||||
if db.stateStore != nil {
|
||||
return db.stateStore
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *nofreezedb) StateStoreReader() ethdb.Reader {
|
||||
if db.stateStore != nil {
|
||||
return db.stateStore
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *nofreezedb) BlockStore() ethdb.Database {
|
||||
if db.blockStore != nil {
|
||||
return db.blockStore
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *nofreezedb) SetBlockStore(block ethdb.Database) {
|
||||
db.blockStore = block
|
||||
}
|
||||
|
||||
func (db *nofreezedb) HasSeparateBlockStore() bool {
|
||||
return db.blockStore != nil
|
||||
}
|
||||
|
||||
func (db *nofreezedb) BlockStoreReader() ethdb.Reader {
|
||||
if db.blockStore != nil {
|
||||
return db.blockStore
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *nofreezedb) BlockStoreWriter() ethdb.Writer {
|
||||
if db.blockStore != nil {
|
||||
return db.blockStore
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReaderOp) error) (err error) {
|
||||
// Unlike other ancient-related methods, this method does not return
|
||||
// errNotSupported when invoked.
|
||||
@@ -201,12 +337,121 @@ func (db *nofreezedb) AncientDatadir() (string, error) {
|
||||
return "", errNotSupported
|
||||
}
|
||||
|
||||
func (db *nofreezedb) SetupFreezerEnv(env *ethdb.FreezerEnv) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDatabase creates a high level database on top of a given key-value data
|
||||
// store without a freezer moving immutable chain segments into cold storage.
|
||||
func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
|
||||
return &nofreezedb{KeyValueStore: db}
|
||||
}
|
||||
|
||||
type emptyfreezedb struct {
|
||||
ethdb.KeyValueStore
|
||||
}
|
||||
|
||||
// HasAncient returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) HasAncient(kind string, number uint64) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// Ancient returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) Ancient(kind string, number uint64) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// AncientRange returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) AncientRange(kind string, start, max, maxByteSize uint64) ([][]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Ancients returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) Ancients() (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// ItemAmountInAncient returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) ItemAmountInAncient() (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Tail returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) Tail() (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// AncientSize returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) AncientSize(kind string) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// ModifyAncients returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// TruncateHead returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) TruncateHead(items uint64) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// TruncateTail returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) TruncateTail(items uint64) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// TruncateTableTail returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) TruncateTableTail(kind string, tail uint64) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// ResetTable returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) ResetTable(kind string, startAt uint64, onlyEmpty bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sync returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (db *emptyfreezedb) DiffStore() ethdb.KeyValueStore { return db }
|
||||
func (db *emptyfreezedb) SetDiffStore(diff ethdb.KeyValueStore) {}
|
||||
func (db *emptyfreezedb) StateStore() ethdb.Database { return db }
|
||||
func (db *emptyfreezedb) GetStateStore() ethdb.Database { return db }
|
||||
func (db *emptyfreezedb) SetStateStore(state ethdb.Database) {}
|
||||
func (db *emptyfreezedb) StateStoreReader() ethdb.Reader { return db }
|
||||
func (db *emptyfreezedb) BlockStore() ethdb.Database { return db }
|
||||
func (db *emptyfreezedb) SetBlockStore(block ethdb.Database) {}
|
||||
func (db *emptyfreezedb) HasSeparateBlockStore() bool { return false }
|
||||
func (db *emptyfreezedb) BlockStoreReader() ethdb.Reader { return db }
|
||||
func (db *emptyfreezedb) BlockStoreWriter() ethdb.Writer { return db }
|
||||
func (db *emptyfreezedb) ReadAncients(fn func(reader ethdb.AncientReaderOp) error) (err error) {
|
||||
return nil
|
||||
}
|
||||
func (db *emptyfreezedb) AncientOffSet() uint64 { return 0 }
|
||||
|
||||
// MigrateTable returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) MigrateTable(kind string, convert convertLegacyFn) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// AncientDatadir returns nil for pruned db that we don't have a backing chain freezer.
|
||||
func (db *emptyfreezedb) AncientDatadir() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
func (db *emptyfreezedb) SetupFreezerEnv(env *ethdb.FreezerEnv) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewEmptyFreezeDB is used for CLI such as `geth db inspect` in pruned db that we don't
|
||||
// have a backing chain freezer.
|
||||
// WARNING: it must be only used in the above case.
|
||||
func NewEmptyFreezeDB(db ethdb.KeyValueStore) ethdb.Database {
|
||||
return &emptyfreezedb{KeyValueStore: db}
|
||||
}
|
||||
|
||||
// NewFreezerDb only create a freezer without statedb.
|
||||
func NewFreezerDb(db ethdb.KeyValueStore, frz, namespace string, readonly bool, newOffSet uint64) (*Freezer, error) {
|
||||
// Create the idle freezer instance, this operation should be atomic to avoid mismatch between offset and acientDB.
|
||||
@@ -247,7 +492,7 @@ func resolveChainFreezerDir(ancient string) string {
|
||||
// value data store with a freezer moving immutable chain segments into cold
|
||||
// storage. The passed ancient indicates the path of root ancient directory
|
||||
// where the chain freezer can be opened.
|
||||
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly, disableFreeze, isLastOffset, pruneAncientData bool) (ethdb.Database, error) {
|
||||
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace string, readonly, disableFreeze, isLastOffset, pruneAncientData, multiDatabase bool) (ethdb.Database, error) {
|
||||
var offset uint64
|
||||
// The offset of ancientDB should be handled differently in different scenarios.
|
||||
if isLastOffset {
|
||||
@@ -256,6 +501,12 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
offset = ReadOffSetOfCurrentAncientFreezer(db)
|
||||
}
|
||||
|
||||
// This case is used for someone who wants to execute geth db inspect CLI in a pruned db
|
||||
if !disableFreeze && readonly && ReadAncientType(db) == PruneFreezerType {
|
||||
log.Warn("Disk db is pruned, using an empty freezer db for CLI")
|
||||
return NewEmptyFreezeDB(db), nil
|
||||
}
|
||||
|
||||
if pruneAncientData && !disableFreeze && !readonly {
|
||||
frdb, err := newPrunedFreezer(resolveChainFreezerDir(ancient), db, offset)
|
||||
if err != nil {
|
||||
@@ -267,9 +518,10 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
WriteAncientType(db, PruneFreezerType)
|
||||
}
|
||||
return &freezerdb{
|
||||
ancientRoot: ancient,
|
||||
KeyValueStore: db,
|
||||
AncientStore: frdb,
|
||||
ancientRoot: ancient,
|
||||
KeyValueStore: db,
|
||||
AncientStore: frdb,
|
||||
AncientFreezer: frdb,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -282,9 +534,18 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
}
|
||||
|
||||
// Create the idle freezer instance
|
||||
frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly, offset)
|
||||
frdb, err := newChainFreezer(resolveChainFreezerDir(ancient), namespace, readonly, offset, multiDatabase)
|
||||
|
||||
// We are creating the freezerdb here because the validation logic for db and freezer below requires certain interfaces
|
||||
// that need a database type. Therefore, we are pre-creating it for subsequent use.
|
||||
freezerDb := &freezerdb{
|
||||
ancientRoot: ancient,
|
||||
KeyValueStore: db,
|
||||
AncientStore: frdb,
|
||||
AncientFreezer: frdb,
|
||||
}
|
||||
if err != nil {
|
||||
printChainMetadata(db)
|
||||
printChainMetadata(freezerDb)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -320,10 +581,10 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
// the freezer and the key-value store.
|
||||
frgenesis, err := frdb.Ancient(ChainFreezerHashTable, 0)
|
||||
if err != nil {
|
||||
printChainMetadata(db)
|
||||
printChainMetadata(freezerDb)
|
||||
return nil, fmt.Errorf("failed to retrieve genesis from ancient %v", err)
|
||||
} else if !bytes.Equal(kvgenesis, frgenesis) {
|
||||
printChainMetadata(db)
|
||||
printChainMetadata(freezerDb)
|
||||
return nil, fmt.Errorf("genesis mismatch: %#x (leveldb) != %#x (ancients)", kvgenesis, frgenesis)
|
||||
}
|
||||
// Key-value store and freezer belong to the same network. Ensure that they
|
||||
@@ -331,7 +592,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
if kvhash, _ := db.Get(headerHashKey(frozen)); len(kvhash) == 0 {
|
||||
// Subsequent header after the freezer limit is missing from the database.
|
||||
// Reject startup if the database has a more recent head.
|
||||
if head := *ReadHeaderNumber(db, ReadHeadHeaderHash(db)); head > frozen-1 {
|
||||
if head := *ReadHeaderNumber(freezerDb, ReadHeadHeaderHash(freezerDb)); head > frozen-1 {
|
||||
// Find the smallest block stored in the key-value store
|
||||
// in range of [frozen, head]
|
||||
var number uint64
|
||||
@@ -341,7 +602,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
}
|
||||
}
|
||||
// We are about to exit on error. Print database metadata before exiting
|
||||
printChainMetadata(db)
|
||||
printChainMetadata(freezerDb)
|
||||
return nil, fmt.Errorf("gap in the chain between ancients [0 - #%d] and leveldb [#%d - #%d] ",
|
||||
frozen-1, number, head)
|
||||
}
|
||||
@@ -356,11 +617,11 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
// store, otherwise we'll end up missing data. We check block #1 to decide
|
||||
// if we froze anything previously or not, but do take care of databases with
|
||||
// only the genesis block.
|
||||
if ReadHeadHeaderHash(db) != common.BytesToHash(kvgenesis) {
|
||||
if ReadHeadHeaderHash(freezerDb) != common.BytesToHash(kvgenesis) {
|
||||
// Key-value store contains more data than the genesis block, make sure we
|
||||
// didn't freeze anything yet.
|
||||
if kvblob, _ := db.Get(headerHashKey(1)); len(kvblob) == 0 {
|
||||
printChainMetadata(db)
|
||||
printChainMetadata(freezerDb)
|
||||
return nil, errors.New("ancient chain segments already extracted, please set --datadir.ancient to the correct path")
|
||||
}
|
||||
// Block #1 is still in the database, we're allowed to init a new freezer
|
||||
@@ -369,6 +630,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
// freezer.
|
||||
}
|
||||
}
|
||||
|
||||
// no prune ancient start success
|
||||
if !readonly {
|
||||
WriteAncientType(db, EntireFreezerType)
|
||||
@@ -381,11 +643,7 @@ func NewDatabaseWithFreezer(db ethdb.KeyValueStore, ancient string, namespace st
|
||||
frdb.wg.Done()
|
||||
}()
|
||||
}
|
||||
return &freezerdb{
|
||||
ancientRoot: ancient,
|
||||
KeyValueStore: db,
|
||||
AncientStore: frdb,
|
||||
}, nil
|
||||
return freezerDb, nil
|
||||
}
|
||||
|
||||
// NewMemoryDatabase creates an ephemeral in-memory key-value database without a
|
||||
@@ -457,16 +715,19 @@ type OpenOptions struct {
|
||||
DisableFreeze bool
|
||||
IsLastOffset bool
|
||||
PruneAncientData bool
|
||||
|
||||
// Ephemeral means that filesystem sync operations should be avoided: data integrity in the face of
|
||||
// a crash is not important. This option should typically be used in tests.
|
||||
Ephemeral bool
|
||||
|
||||
MultiDataBase bool
|
||||
}
|
||||
|
||||
// openKeyValueDatabase opens a disk-based key-value database, e.g. leveldb or pebble.
|
||||
//
|
||||
// type == null type != null
|
||||
// +----------------------------------------
|
||||
// db is non-existent | leveldb default | specified type
|
||||
// db is non-existent | pebble default | specified type
|
||||
// db is existent | from db | specified type (if compatible)
|
||||
func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
|
||||
// Reject any unsupported database type
|
||||
@@ -487,12 +748,9 @@ func openKeyValueDatabase(o OpenOptions) (ethdb.Database, error) {
|
||||
log.Info("Using leveldb as the backing database")
|
||||
return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
||||
}
|
||||
// No pre-existing database, no user-requested one either. Default to Pebble
|
||||
// on supported platforms and LevelDB on anything else.
|
||||
// log.Info("Defaulting to pebble as the backing database")
|
||||
// return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
||||
log.Info("Defaulting to leveldb as the backing database")
|
||||
return NewLevelDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly)
|
||||
// No pre-existing database, no user-requested one either. Default to Pebble.
|
||||
log.Info("Defaulting to pebble as the backing database")
|
||||
return NewPebbleDBDatabase(o.Directory, o.Cache, o.Handles, o.Namespace, o.ReadOnly, o.Ephemeral)
|
||||
}
|
||||
|
||||
// Open opens both a disk-based key-value database such as leveldb or pebble, but also
|
||||
@@ -507,13 +765,13 @@ func Open(o OpenOptions) (ethdb.Database, error) {
|
||||
}
|
||||
if ReadAncientType(kvdb) == PruneFreezerType {
|
||||
if !o.PruneAncientData {
|
||||
log.Warn("Disk db is pruned")
|
||||
log.Warn("NOTICE: You're opening a pruned disk db!")
|
||||
}
|
||||
}
|
||||
if len(o.AncientsDirectory) == 0 {
|
||||
return kvdb, nil
|
||||
}
|
||||
frdb, err := NewDatabaseWithFreezer(kvdb, o.AncientsDirectory, o.Namespace, o.ReadOnly, o.DisableFreeze, o.IsLastOffset, o.PruneAncientData)
|
||||
frdb, err := NewDatabaseWithFreezer(kvdb, o.AncientsDirectory, o.Namespace, o.ReadOnly, o.DisableFreeze, o.IsLastOffset, o.PruneAncientData, o.MultiDataBase)
|
||||
if err != nil {
|
||||
kvdb.Close()
|
||||
return nil, err
|
||||
@@ -555,7 +813,7 @@ func AncientInspect(db ethdb.Database) error {
|
||||
offset := counter(ReadOffSetOfCurrentAncientFreezer(db))
|
||||
// Get number of ancient rows inside the freezer.
|
||||
ancients := counter(0)
|
||||
if count, err := db.ItemAmountInAncient(); err != nil {
|
||||
if count, err := db.BlockStore().ItemAmountInAncient(); err != nil {
|
||||
log.Error("failed to get the items amount in ancientDB", "err", err)
|
||||
return err
|
||||
} else {
|
||||
@@ -603,12 +861,64 @@ func PruneHashTrieNodeInDataBase(db ethdb.Database) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type DataType int
|
||||
|
||||
const (
|
||||
StateDataType DataType = iota
|
||||
BlockDataType
|
||||
ChainDataType
|
||||
Unknown
|
||||
)
|
||||
|
||||
func DataTypeByKey(key []byte) DataType {
|
||||
switch {
|
||||
// state
|
||||
case IsLegacyTrieNode(key, key),
|
||||
bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength,
|
||||
IsAccountTrieNode(key),
|
||||
IsStorageTrieNode(key):
|
||||
return StateDataType
|
||||
|
||||
// block
|
||||
case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength),
|
||||
bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength),
|
||||
bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength),
|
||||
bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix),
|
||||
bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix),
|
||||
bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||
return BlockDataType
|
||||
default:
|
||||
for _, meta := range [][]byte{
|
||||
fastTrieProgressKey, persistentStateIDKey, trieJournalKey, snapSyncStatusFlagKey} {
|
||||
if bytes.Equal(key, meta) {
|
||||
return StateDataType
|
||||
}
|
||||
}
|
||||
for _, meta := range [][]byte{headHeaderKey, headFinalizedBlockKey, headBlockKey, headFastBlockKey} {
|
||||
if bytes.Equal(key, meta) {
|
||||
return BlockDataType
|
||||
}
|
||||
}
|
||||
return ChainDataType
|
||||
}
|
||||
}
|
||||
|
||||
// InspectDatabase traverses the entire database and checks the size
|
||||
// of all different categories of data.
|
||||
func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
it := db.NewIterator(keyPrefix, keyStart)
|
||||
defer it.Release()
|
||||
|
||||
var trieIter ethdb.Iterator
|
||||
var blockIter ethdb.Iterator
|
||||
if db.StateStore() != nil {
|
||||
trieIter = db.StateStore().NewIterator(keyPrefix, nil)
|
||||
defer trieIter.Release()
|
||||
}
|
||||
if db.HasSeparateBlockStore() {
|
||||
blockIter = db.BlockStore().NewIterator(keyPrefix, nil)
|
||||
defer blockIter.Release()
|
||||
}
|
||||
var (
|
||||
count int64
|
||||
start = time.Now()
|
||||
@@ -659,14 +969,14 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
bodies.Add(size)
|
||||
case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
|
||||
receipts.Add(size)
|
||||
case IsLegacyTrieNode(key, it.Value()):
|
||||
legacyTries.Add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
|
||||
tds.Add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
|
||||
numHashPairings.Add(size)
|
||||
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||
hashNumPairings.Add(size)
|
||||
case IsLegacyTrieNode(key, it.Value()):
|
||||
legacyTries.Add(size)
|
||||
case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
|
||||
stateLookups.Add(size)
|
||||
case IsAccountTrieNode(key):
|
||||
@@ -728,6 +1038,96 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
logged = time.Now()
|
||||
}
|
||||
}
|
||||
// inspect separate trie db
|
||||
if trieIter != nil {
|
||||
count = 0
|
||||
logged = time.Now()
|
||||
for trieIter.Next() {
|
||||
var (
|
||||
key = trieIter.Key()
|
||||
value = trieIter.Value()
|
||||
size = common.StorageSize(len(key) + len(value))
|
||||
)
|
||||
total += size
|
||||
|
||||
switch {
|
||||
case IsLegacyTrieNode(key, value):
|
||||
legacyTries.Add(size)
|
||||
case bytes.HasPrefix(key, stateIDPrefix) && len(key) == len(stateIDPrefix)+common.HashLength:
|
||||
stateLookups.Add(size)
|
||||
case IsAccountTrieNode(key):
|
||||
accountTries.Add(size)
|
||||
case IsStorageTrieNode(key):
|
||||
storageTries.Add(size)
|
||||
default:
|
||||
var accounted bool
|
||||
for _, meta := range [][]byte{
|
||||
fastTrieProgressKey, persistentStateIDKey, trieJournalKey, snapSyncStatusFlagKey} {
|
||||
if bytes.Equal(key, meta) {
|
||||
metadata.Add(size)
|
||||
accounted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !accounted {
|
||||
unaccounted.Add(size)
|
||||
}
|
||||
}
|
||||
count++
|
||||
if count%1000 == 0 && time.Since(logged) > 8*time.Second {
|
||||
log.Info("Inspecting separate state database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logged = time.Now()
|
||||
}
|
||||
}
|
||||
log.Info("Inspecting separate state database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
// inspect separate block db
|
||||
if blockIter != nil {
|
||||
count = 0
|
||||
logged = time.Now()
|
||||
|
||||
for blockIter.Next() {
|
||||
var (
|
||||
key = blockIter.Key()
|
||||
value = blockIter.Value()
|
||||
size = common.StorageSize(len(key) + len(value))
|
||||
)
|
||||
total += size
|
||||
|
||||
switch {
|
||||
case bytes.HasPrefix(key, headerPrefix) && len(key) == (len(headerPrefix)+8+common.HashLength):
|
||||
headers.Add(size)
|
||||
case bytes.HasPrefix(key, blockBodyPrefix) && len(key) == (len(blockBodyPrefix)+8+common.HashLength):
|
||||
bodies.Add(size)
|
||||
case bytes.HasPrefix(key, blockReceiptsPrefix) && len(key) == (len(blockReceiptsPrefix)+8+common.HashLength):
|
||||
receipts.Add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerTDSuffix):
|
||||
tds.Add(size)
|
||||
case bytes.HasPrefix(key, headerPrefix) && bytes.HasSuffix(key, headerHashSuffix):
|
||||
numHashPairings.Add(size)
|
||||
case bytes.HasPrefix(key, headerNumberPrefix) && len(key) == (len(headerNumberPrefix)+common.HashLength):
|
||||
hashNumPairings.Add(size)
|
||||
default:
|
||||
var accounted bool
|
||||
for _, meta := range [][]byte{headHeaderKey, headFinalizedBlockKey, headBlockKey, headFastBlockKey} {
|
||||
if bytes.Equal(key, meta) {
|
||||
metadata.Add(size)
|
||||
accounted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !accounted {
|
||||
unaccounted.Add(size)
|
||||
}
|
||||
}
|
||||
count++
|
||||
if count%1000 == 0 && time.Since(logged) > 8*time.Second {
|
||||
log.Info("Inspecting separate block database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logged = time.Now()
|
||||
}
|
||||
}
|
||||
log.Info("Inspecting separate block database", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
}
|
||||
// Display the database statistic of key-value store.
|
||||
stats := [][]string{
|
||||
{"Key-Value store", "Headers", headers.Size(), headers.Count()},
|
||||
@@ -753,7 +1153,7 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
{"Light client", "Bloom trie nodes", bloomTrieNodes.Size(), bloomTrieNodes.Count()},
|
||||
}
|
||||
// Inspect all registered append-only file store then.
|
||||
ancients, err := inspectFreezers(db)
|
||||
ancients, err := inspectFreezers(db.BlockStore())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -768,6 +1168,28 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
}
|
||||
total += ancient.size()
|
||||
}
|
||||
|
||||
// inspect ancient state in separate trie db if exist
|
||||
if trieIter != nil {
|
||||
stateAncients, err := inspectFreezers(db.StateStore())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, ancient := range stateAncients {
|
||||
for _, table := range ancient.sizes {
|
||||
if ancient.name == "chain" {
|
||||
break
|
||||
}
|
||||
stats = append(stats, []string{
|
||||
fmt.Sprintf("Ancient store (%s)", strings.Title(ancient.name)),
|
||||
strings.Title(table.name),
|
||||
table.size.String(),
|
||||
fmt.Sprintf("%d", ancient.count()),
|
||||
})
|
||||
}
|
||||
total += ancient.size()
|
||||
}
|
||||
}
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
table.SetHeader([]string{"Database", "Category", "Size", "Items"})
|
||||
table.SetFooter([]string{"", "Total", total.String(), " "})
|
||||
@@ -780,8 +1202,64 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func DeleteTrieState(db ethdb.Database) error {
|
||||
var (
|
||||
it ethdb.Iterator
|
||||
batch = db.NewBatch()
|
||||
start = time.Now()
|
||||
logged = time.Now()
|
||||
count int64
|
||||
key []byte
|
||||
)
|
||||
|
||||
prefixKeys := map[string]func([]byte) bool{
|
||||
string(trieNodeAccountPrefix): IsAccountTrieNode,
|
||||
string(trieNodeStoragePrefix): IsStorageTrieNode,
|
||||
string(stateIDPrefix): func(key []byte) bool { return len(key) == len(stateIDPrefix)+common.HashLength },
|
||||
}
|
||||
|
||||
for prefix, isValid := range prefixKeys {
|
||||
it = db.NewIterator([]byte(prefix), nil)
|
||||
|
||||
for it.Next() {
|
||||
key = it.Key()
|
||||
if !isValid(key) {
|
||||
continue
|
||||
}
|
||||
|
||||
batch.Delete(it.Key())
|
||||
if batch.ValueSize() > ethdb.IdealBatchSize {
|
||||
if err := batch.Write(); err != nil {
|
||||
it.Release()
|
||||
return err
|
||||
}
|
||||
batch.Reset()
|
||||
}
|
||||
|
||||
count++
|
||||
if time.Since(logged) > 8*time.Second {
|
||||
log.Info("Deleting trie state", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
logged = time.Now()
|
||||
}
|
||||
}
|
||||
|
||||
it.Release()
|
||||
}
|
||||
|
||||
if batch.ValueSize() > 0 {
|
||||
if err := batch.Write(); err != nil {
|
||||
return err
|
||||
}
|
||||
batch.Reset()
|
||||
}
|
||||
|
||||
log.Info("Deleted trie state", "count", count, "elapsed", common.PrettyDuration(time.Since(start)))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// printChainMetadata prints out chain metadata to stderr.
|
||||
func printChainMetadata(db ethdb.KeyValueStore) {
|
||||
func printChainMetadata(db ethdb.Reader) {
|
||||
fmt.Fprintf(os.Stderr, "Chain metadata\n")
|
||||
for _, v := range ReadChainMetadata(db) {
|
||||
fmt.Fprintf(os.Stderr, " %s\n", strings.Join(v, ": "))
|
||||
@@ -792,7 +1270,7 @@ func printChainMetadata(db ethdb.KeyValueStore) {
|
||||
// ReadChainMetadata returns a set of key/value pairs that contains information
|
||||
// about the database chain status. This can be used for diagnostic purposes
|
||||
// when investigating the state of the node.
|
||||
func ReadChainMetadata(db ethdb.KeyValueStore) [][]string {
|
||||
func ReadChainMetadata(db ethdb.Reader) [][]string {
|
||||
pp := func(val *uint64) string {
|
||||
if val == nil {
|
||||
return "<nil>"
|
||||
|
||||
@@ -26,6 +26,8 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/log"
|
||||
@@ -88,6 +90,7 @@ func NewChainFreezer(datadir string, namespace string, readonly bool, offset uin
|
||||
//
|
||||
// The 'tables' argument defines the data tables. If the value of a map
|
||||
// entry is true, snappy compression is disabled for the table.
|
||||
// additionTables indicates the new add tables for freezerDB, it has some special rules.
|
||||
func NewFreezer(datadir string, namespace string, readonly bool, offset uint64, maxTableSize uint32, tables map[string]bool) (*Freezer, error) {
|
||||
// Create the initial freezer object
|
||||
var (
|
||||
@@ -128,7 +131,15 @@ func NewFreezer(datadir string, namespace string, readonly bool, offset uint64,
|
||||
|
||||
// Create the tables.
|
||||
for name, disableSnappy := range tables {
|
||||
table, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly)
|
||||
var (
|
||||
table *freezerTable
|
||||
err error
|
||||
)
|
||||
if slices.Contains(additionTables, name) {
|
||||
table, err = openAdditionTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly)
|
||||
} else {
|
||||
table, err = newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly)
|
||||
}
|
||||
if err != nil {
|
||||
for _, table := range freezer.tables {
|
||||
table.Close()
|
||||
@@ -167,6 +178,20 @@ func NewFreezer(datadir string, namespace string, readonly bool, offset uint64,
|
||||
return freezer, nil
|
||||
}
|
||||
|
||||
// openAdditionTable create table, it will auto create new files when it was first initialized
|
||||
func openAdditionTable(datadir, name string, readMeter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, maxTableSize uint32, disableSnappy, readonly bool) (*freezerTable, error) {
|
||||
if readonly {
|
||||
f, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = f.Close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy, readonly)
|
||||
}
|
||||
|
||||
// Close terminates the chain freezer, unmapping all the data files.
|
||||
func (f *Freezer) Close() error {
|
||||
f.writeLock.Lock()
|
||||
@@ -214,7 +239,7 @@ func (f *Freezer) Ancient(kind string, number uint64) ([]byte, error) {
|
||||
// - if maxBytes is not specified, 'count' items will be returned if they are present.
|
||||
func (f *Freezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
|
||||
if table := f.tables[kind]; table != nil {
|
||||
return table.RetrieveItems(start, count, maxBytes)
|
||||
return table.RetrieveItems(start-f.offset, count, maxBytes)
|
||||
}
|
||||
return nil, errUnknownTable
|
||||
}
|
||||
@@ -224,6 +249,12 @@ func (f *Freezer) Ancients() (uint64, error) {
|
||||
return f.frozen.Load(), nil
|
||||
}
|
||||
|
||||
func (f *Freezer) TableAncients(kind string) (uint64, error) {
|
||||
f.writeLock.RLock()
|
||||
defer f.writeLock.RUnlock()
|
||||
return f.tables[kind].items.Load() + f.offset, nil
|
||||
}
|
||||
|
||||
// ItemAmountInAncient returns the actual length of current ancientDB.
|
||||
func (f *Freezer) ItemAmountInAncient() (uint64, error) {
|
||||
return f.frozen.Load() - atomic.LoadUint64(&f.offset), nil
|
||||
@@ -308,8 +339,23 @@ func (f *Freezer) TruncateHead(items uint64) (uint64, error) {
|
||||
if oitems <= items {
|
||||
return oitems, nil
|
||||
}
|
||||
for _, table := range f.tables {
|
||||
if err := table.truncateHead(items - f.offset); err != nil {
|
||||
for kind, table := range f.tables {
|
||||
err := table.truncateHead(items - f.offset)
|
||||
if err == errTruncationBelowTail {
|
||||
// This often happens in chain rewinds, but the blob table is special.
|
||||
// It has the same head, but a different tail from other tables (like bodies, receipts).
|
||||
// So if the chain is rewound to head below the blob's tail, it needs to reset again.
|
||||
if kind != ChainFreezerBlobSidecarTable {
|
||||
return 0, err
|
||||
}
|
||||
nt, err := table.resetItems(items - f.offset)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
f.tables[kind] = nt
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
@@ -365,6 +411,10 @@ func (f *Freezer) validate() error {
|
||||
)
|
||||
// Hack to get boundary of any table
|
||||
for kind, table := range f.tables {
|
||||
// addition tables is special cases
|
||||
if slices.Contains(additionTables, kind) {
|
||||
continue
|
||||
}
|
||||
head = table.items.Load()
|
||||
tail = table.itemHidden.Load()
|
||||
name = kind
|
||||
@@ -372,6 +422,21 @@ func (f *Freezer) validate() error {
|
||||
}
|
||||
// Now check every table against those boundaries.
|
||||
for kind, table := range f.tables {
|
||||
// check addition tables, try to align with exist tables
|
||||
if slices.Contains(additionTables, kind) {
|
||||
// if the table is empty, just skip
|
||||
if EmptyTable(table) {
|
||||
continue
|
||||
}
|
||||
// otherwise, just align head
|
||||
if head != table.items.Load() {
|
||||
return fmt.Errorf("freezer tables %s and %s have differing head: %d != %d", kind, name, table.items.Load(), head)
|
||||
}
|
||||
if tail > table.itemHidden.Load() {
|
||||
return fmt.Errorf("freezer tables %s and %s have differing tail: %d != %d", kind, name, table.itemHidden.Load(), tail)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if head != table.items.Load() {
|
||||
return fmt.Errorf("freezer tables %s and %s have differing head: %d != %d", kind, name, table.items.Load(), head)
|
||||
}
|
||||
@@ -390,7 +455,18 @@ func (f *Freezer) repair() error {
|
||||
head = uint64(math.MaxUint64)
|
||||
tail = uint64(0)
|
||||
)
|
||||
for _, table := range f.tables {
|
||||
for kind, table := range f.tables {
|
||||
// addition tables only align head
|
||||
if slices.Contains(additionTables, kind) {
|
||||
if EmptyTable(table) {
|
||||
continue
|
||||
}
|
||||
items := table.items.Load()
|
||||
if head > items {
|
||||
head = items
|
||||
}
|
||||
continue
|
||||
}
|
||||
items := table.items.Load()
|
||||
if head > items {
|
||||
head = items
|
||||
@@ -400,8 +476,27 @@ func (f *Freezer) repair() error {
|
||||
tail = hidden
|
||||
}
|
||||
}
|
||||
for _, table := range f.tables {
|
||||
if err := table.truncateHead(head); err != nil {
|
||||
for kind, table := range f.tables {
|
||||
// try to align with exist tables, skip empty table
|
||||
if slices.Contains(additionTables, kind) && EmptyTable(table) {
|
||||
continue
|
||||
}
|
||||
err := table.truncateHead(head)
|
||||
if err == errTruncationBelowTail {
|
||||
// This often happens in chain rewinds, but the blob table is special.
|
||||
// It has the same head, but a different tail from other tables (like bodies, receipts).
|
||||
// So if the chain is rewound to head below the blob's tail, it needs to reset again.
|
||||
if kind != ChainFreezerBlobSidecarTable {
|
||||
return err
|
||||
}
|
||||
nt, err := table.resetItems(head)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.tables[kind] = nt
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := table.truncateTail(tail); err != nil {
|
||||
@@ -446,41 +541,6 @@ func gcKvStore(db ethdb.KeyValueStore, ancients []common.Hash, first uint64, fro
|
||||
}
|
||||
batch.Reset()
|
||||
|
||||
// Step into the future and delete and dangling side chains
|
||||
if frozen > 0 {
|
||||
tip := frozen
|
||||
nfdb := &nofreezedb{KeyValueStore: db}
|
||||
for len(dangling) > 0 {
|
||||
drop := make(map[common.Hash]struct{})
|
||||
for _, hash := range dangling {
|
||||
log.Debug("Dangling parent from freezer", "number", tip-1, "hash", hash)
|
||||
drop[hash] = struct{}{}
|
||||
}
|
||||
children := ReadAllHashes(db, tip)
|
||||
for i := 0; i < len(children); i++ {
|
||||
// Dig up the child and ensure it's dangling
|
||||
child := ReadHeader(nfdb, children[i], tip)
|
||||
if child == nil {
|
||||
log.Error("Missing dangling header", "number", tip, "hash", children[i])
|
||||
continue
|
||||
}
|
||||
if _, ok := drop[child.ParentHash]; !ok {
|
||||
children = append(children[:i], children[i+1:]...)
|
||||
i--
|
||||
continue
|
||||
}
|
||||
// Delete all block data associated with the child
|
||||
log.Debug("Deleting dangling block", "number", tip, "hash", children[i], "parent", child.ParentHash)
|
||||
DeleteBlock(batch, children[i], tip)
|
||||
}
|
||||
dangling = children
|
||||
tip++
|
||||
}
|
||||
if err := batch.Write(); err != nil {
|
||||
log.Crit("Failed to delete dangling side blocks", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Log something friendly for the user
|
||||
context := []interface{}{
|
||||
"blocks", frozen - first, "elapsed", common.PrettyDuration(time.Since(start)), "number", frozen - 1,
|
||||
@@ -602,3 +662,78 @@ func (f *Freezer) MigrateTable(kind string, convert convertLegacyFn) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TruncateTableTail will truncate certain table to new tail
|
||||
func (f *Freezer) TruncateTableTail(kind string, tail uint64) (uint64, error) {
|
||||
if f.readonly {
|
||||
return 0, errReadOnly
|
||||
}
|
||||
|
||||
f.writeLock.Lock()
|
||||
defer f.writeLock.Unlock()
|
||||
|
||||
if !slices.Contains(additionTables, kind) {
|
||||
return 0, errors.New("only new added table could be truncated independently")
|
||||
}
|
||||
if tail < f.offset {
|
||||
return 0, errors.New("the input tail&head is less than offset")
|
||||
}
|
||||
t, exist := f.tables[kind]
|
||||
if !exist {
|
||||
return 0, errors.New("you reset a non-exist table")
|
||||
}
|
||||
|
||||
old := t.itemHidden.Load() + f.offset
|
||||
if err := t.truncateTail(tail - f.offset); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return old, nil
|
||||
}
|
||||
|
||||
// ResetTable will reset certain table with new start point
|
||||
// only used for ChainFreezerBlobSidecarTable now
|
||||
func (f *Freezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error {
|
||||
if f.readonly {
|
||||
return errReadOnly
|
||||
}
|
||||
|
||||
f.writeLock.Lock()
|
||||
defer f.writeLock.Unlock()
|
||||
|
||||
t, exist := f.tables[kind]
|
||||
if !exist {
|
||||
return errors.New("you reset a non-exist table")
|
||||
}
|
||||
|
||||
// if you reset a non empty table just skip
|
||||
if onlyEmpty && !EmptyTable(t) {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := f.Sync(); err != nil {
|
||||
return err
|
||||
}
|
||||
nt, err := t.resetItems(startAt - f.offset)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
f.tables[kind] = nt
|
||||
|
||||
// repair all tables with same tail & head
|
||||
if err := f.repair(); err != nil {
|
||||
for _, table := range f.tables {
|
||||
table.Close()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
f.frozen.Add(f.offset)
|
||||
f.tail.Add(f.offset)
|
||||
f.writeBatch = newFreezerBatch(f)
|
||||
log.Debug("Reset Table", "kind", kind, "tail", f.tables[kind].itemHidden.Load(), "frozen", f.tables[kind].items.Load())
|
||||
return nil
|
||||
}
|
||||
|
||||
func EmptyTable(t *freezerTable) bool {
|
||||
return t.items.Load() == 0
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/golang/snappy"
|
||||
@@ -65,6 +67,10 @@ func (batch *freezerBatch) commit() (item uint64, writeSize int64, err error) {
|
||||
// Check that count agrees on all batches.
|
||||
item = uint64(math.MaxUint64)
|
||||
for name, tb := range batch.tables {
|
||||
// skip empty addition tables
|
||||
if slices.Contains(additionTables, name) && EmptyTable(tb.t) {
|
||||
continue
|
||||
}
|
||||
if item < math.MaxUint64 && tb.curItem != item {
|
||||
return 0, 0, fmt.Errorf("table %s is at item %d, want %d", name, tb.curItem, item)
|
||||
}
|
||||
|
||||
@@ -205,6 +205,22 @@ func (f *ResettableFreezer) TruncateTail(tail uint64) (uint64, error) {
|
||||
return f.freezer.TruncateTail(tail)
|
||||
}
|
||||
|
||||
// TruncateTableTail will truncate certain table to new tail
|
||||
func (f *ResettableFreezer) TruncateTableTail(kind string, tail uint64) (uint64, error) {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
|
||||
return f.freezer.TruncateTableTail(kind, tail)
|
||||
}
|
||||
|
||||
// ResetTable will reset certain table with new start point
|
||||
func (f *ResettableFreezer) ResetTable(kind string, startAt uint64, onlyEmpty bool) error {
|
||||
f.lock.RLock()
|
||||
defer f.lock.RUnlock()
|
||||
|
||||
return f.freezer.ResetTable(kind, startAt, onlyEmpty)
|
||||
}
|
||||
|
||||
// Sync flushes all data tables to disk.
|
||||
func (f *ResettableFreezer) Sync() error {
|
||||
f.lock.RLock()
|
||||
|
||||
@@ -44,6 +44,8 @@ var (
|
||||
|
||||
// errNotSupported is returned if the database doesn't support the required operation.
|
||||
errNotSupported = errors.New("this operation is not supported")
|
||||
|
||||
errTruncationBelowTail = errors.New("truncation below tail")
|
||||
)
|
||||
|
||||
// indexEntry contains the number/id of the file that the data resides in, as well as the
|
||||
@@ -125,6 +127,11 @@ func newFreezerTable(path, name string, disableSnappy, readonly bool) (*freezerT
|
||||
return newTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, freezerTableSize, disableSnappy, readonly)
|
||||
}
|
||||
|
||||
// newAdditionTable opens the given path as a addition table.
|
||||
func newAdditionTable(path, name string, disableSnappy, readonly bool) (*freezerTable, error) {
|
||||
return openAdditionTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, freezerTableSize, disableSnappy, readonly)
|
||||
}
|
||||
|
||||
// newTable opens a freezer table, creating the data and index files if they are
|
||||
// non-existent. Both files are truncated to the shortest common length to ensure
|
||||
// they don't go out of sync.
|
||||
@@ -406,7 +413,7 @@ func (t *freezerTable) truncateHead(items uint64) error {
|
||||
return nil
|
||||
}
|
||||
if items < t.itemHidden.Load() {
|
||||
return errors.New("truncation below tail")
|
||||
return errTruncationBelowTail
|
||||
}
|
||||
// We need to truncate, save the old size for metrics tracking
|
||||
oldSize, err := t.sizeNolock()
|
||||
@@ -1004,7 +1011,7 @@ func (t *freezerTable) ResetItemsOffset(virtualTail uint64) error {
|
||||
}
|
||||
|
||||
if stat.Size() == 0 {
|
||||
return fmt.Errorf("Stat size is zero when ResetVirtualTail.")
|
||||
return errors.New("Stat size is zero when ResetVirtualTail.")
|
||||
}
|
||||
|
||||
var firstIndex indexEntry
|
||||
@@ -1026,3 +1033,54 @@ func (t *freezerTable) ResetItemsOffset(virtualTail uint64) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// resetItems reset freezer table to 0 items with new startAt
|
||||
// only used for ChainFreezerBlobSidecarTable now
|
||||
func (t *freezerTable) resetItems(startAt uint64) (*freezerTable, error) {
|
||||
t.lock.Lock()
|
||||
defer t.lock.Unlock()
|
||||
if t.readonly {
|
||||
return nil, errors.New("resetItems in readonly mode")
|
||||
}
|
||||
|
||||
// remove all data files
|
||||
t.head.Close()
|
||||
t.releaseFilesAfter(0, true)
|
||||
t.releaseFile(0)
|
||||
|
||||
// overwrite metadata file
|
||||
if err := writeMetadata(t.meta, newMetadata(startAt)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := t.meta.Sync(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t.meta.Close()
|
||||
|
||||
// recreate the index file
|
||||
t.index.Close()
|
||||
os.Remove(t.index.Name())
|
||||
var idxName string
|
||||
if t.noCompression {
|
||||
idxName = fmt.Sprintf("%s.ridx", t.name) // raw index file
|
||||
} else {
|
||||
idxName = fmt.Sprintf("%s.cidx", t.name) // compressed index file
|
||||
}
|
||||
index, err := openFreezerFileForAppend(filepath.Join(t.path, idxName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tailIndex := indexEntry{
|
||||
filenum: 0,
|
||||
offset: uint32(startAt),
|
||||
}
|
||||
if _, err = index.Write(tailIndex.append(nil)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := index.Sync(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
index.Close()
|
||||
|
||||
return newFreezerTable(t.path, t.name, t.noCompression, t.readonly)
|
||||
}
|
||||
|
||||
@@ -897,7 +897,7 @@ func getChunk(size int, b int) []byte {
|
||||
}
|
||||
|
||||
// TODO (?)
|
||||
// - test that if we remove several head-files, aswell as data last data-file,
|
||||
// - test that if we remove several head-files, as well as data last data-file,
|
||||
// the index is truncated accordingly
|
||||
// Right now, the freezer would fail on these conditions:
|
||||
// 1. have data files d0, d1, d2, d3
|
||||
@@ -1370,3 +1370,76 @@ func TestRandom(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResetItems(t *testing.T) {
|
||||
t.Parallel()
|
||||
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
|
||||
fname := fmt.Sprintf("truncate-tail-%d", rand.Uint64())
|
||||
|
||||
// Fill table
|
||||
f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Write 7 x 20 bytes, splitting out into four files
|
||||
batch := f.newBatch(0)
|
||||
require.NoError(t, batch.AppendRaw(0, getChunk(20, 0x00)))
|
||||
require.NoError(t, batch.AppendRaw(1, getChunk(20, 0x11)))
|
||||
require.NoError(t, batch.AppendRaw(2, getChunk(20, 0x22)))
|
||||
require.NoError(t, batch.AppendRaw(3, getChunk(20, 0x33)))
|
||||
require.NoError(t, batch.AppendRaw(4, getChunk(20, 0x44)))
|
||||
require.NoError(t, batch.AppendRaw(5, getChunk(20, 0x55)))
|
||||
require.NoError(t, batch.AppendRaw(6, getChunk(20, 0x66)))
|
||||
require.NoError(t, batch.commit())
|
||||
|
||||
// nothing to do, all the items should still be there.
|
||||
f, err = f.resetItems(0)
|
||||
require.NoError(t, err)
|
||||
f, err = f.resetItems(8)
|
||||
require.NoError(t, err)
|
||||
f, err = f.resetItems(7)
|
||||
require.NoError(t, err)
|
||||
fmt.Println(f.dumpIndexString(0, 1000))
|
||||
checkRetrieveError(t, f, map[uint64]error{
|
||||
0: errOutOfBounds,
|
||||
6: errOutOfBounds,
|
||||
})
|
||||
|
||||
// append
|
||||
batch = f.newBatch(0)
|
||||
require.Error(t, batch.AppendRaw(0, getChunk(20, 0xa0)))
|
||||
require.NoError(t, batch.AppendRaw(7, getChunk(20, 0x77)))
|
||||
require.NoError(t, batch.AppendRaw(8, getChunk(20, 0x88)))
|
||||
require.NoError(t, batch.AppendRaw(9, getChunk(20, 0x99)))
|
||||
require.NoError(t, batch.commit())
|
||||
fmt.Println(f.dumpIndexString(0, 1000))
|
||||
checkRetrieve(t, f, map[uint64][]byte{
|
||||
7: getChunk(20, 0x77),
|
||||
9: getChunk(20, 0x99),
|
||||
})
|
||||
|
||||
// Reopen the table, the deletion information should be persisted as well
|
||||
f.Close()
|
||||
f, err = newTable(os.TempDir(), fname, rm, wm, sg, 40, true, false)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
fmt.Println(f.dumpIndexString(0, 1000))
|
||||
checkRetrieveError(t, f, map[uint64]error{
|
||||
0: errOutOfBounds,
|
||||
6: errOutOfBounds,
|
||||
10: errOutOfBounds,
|
||||
})
|
||||
checkRetrieve(t, f, map[uint64][]byte{
|
||||
7: getChunk(20, 0x77),
|
||||
9: getChunk(20, 0x99),
|
||||
})
|
||||
|
||||
// truncate all, the entire freezer should be deleted
|
||||
f.truncateTail(10)
|
||||
checkRetrieveError(t, f, map[uint64]error{
|
||||
0: errOutOfBounds,
|
||||
9: errOutOfBounds,
|
||||
})
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user