Compare commits

..

509 Commits

Author SHA1 Message Date
Georgios Konstantopoulos
378c5851d5 wip 2025-12-18 19:22:54 -05:00
Arsenii Kulikov
30162c535e perf: properly share precompile cache + use moka (#20502) 2025-12-18 22:42:44 +00:00
Federico Gimenez
cd8fec3273 feat(stages): use EitherWriter for TransactionLookupStage RocksDB writes (#20428) 2025-12-18 21:34:17 +00:00
Tomass
1e38c7fea8 chore(hardforks): drop unnecessary field reassignment in TTD branch (#20457)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-18 21:02:56 +00:00
Block Wizard
4dfaf238c9 chore(net): fix misleading comment about uncompressed message size check (#19510) 2025-12-18 20:34:50 +00:00
forkfury
4cf36dda54 docs: correct FinishedStateUpdates message name (#20471) 2025-12-18 20:16:15 +00:00
phrwlk
41ce3d3bbf docs: fix Docker db-access troubleshooting example (#20483) 2025-12-18 20:13:01 +00:00
sashass1315
429d13772e chore(cli): correct p2p body error message (#20498) 2025-12-18 20:01:59 +00:00
Gigi
0cbf89193d docs: correct intra-doc link references (#20467) 2025-12-18 19:56:57 +00:00
radik878
0c3c42bffe chore(primitives-traits): correct SealedBlock::senders return description (#20465) 2025-12-18 19:56:22 +00:00
cui
cdbbd08677 fix: session config should be read from config file (#20484)
Co-authored-by: weixie.cui <weixie.cui@okg.com>
2025-12-18 19:53:18 +00:00
Alexey Shekhirin
4adb1fa5ac fix(cli): default to 0 genesis block number (#20494) 2025-12-18 15:07:59 +00:00
Brian Picciano
b3a792ad1e fix(engine): Use OverlayStateProviderFactory for state root fallback (#20462) 2025-12-18 14:30:11 +00:00
Arsenii Kulikov
98a7095c7a fix: properly determine first stage during pipeline consistency check (#20460) 2025-12-18 10:43:08 +00:00
Matthias Seitz
701e5ec455 chore: add engine terminate (#20420)
Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com>
2025-12-18 09:01:36 +00:00
Lorsmirq Benton
8e00e81af4 docs: remove orphaned debug.mdx (#20474) 2025-12-18 04:14:23 +00:00
YK
453514c48f perf(engine): share Arc<ExecutionOutcome> to avoid cloning BundleState (#20448) 2025-12-18 01:07:18 +00:00
James Niken
432ac7afa1 chore: fix blob count in validation benchmark (#20456) 2025-12-18 00:51:45 +00:00
Emilia Hane
c7fca9f2b4 chore(node): Report actual gas price to ethstats (#20461)
Co-authored-by: Rifvck Zieger <rifvckzieger@gmail.com>
2025-12-18 00:50:16 +00:00
DaniPopes
715ca5b980 chore: simplify prewarm state providers (#20469) 2025-12-17 22:11:11 +00:00
Federico Gimenez
9ae62aad26 feat(storage): add method to check invariants on RocksDB tables (#20340) 2025-12-17 20:26:51 +00:00
YK
c65df40526 perf: remove redundant contains_key check in ProofSequencer::add_proof (#20459) 2025-12-17 13:58:59 +00:00
Vui-Chee
d8acc1e4cf feat: support non-zero genesis block numbers (#19877)
Co-authored-by: JimmyShi22 <417711026@qq.com>
2025-12-17 11:03:12 +00:00
sashass1315
852aad8126 docs(exex): document ChainRevert flow in how-it-works (#20455) 2025-12-17 10:28:49 +00:00
Karl Yu
61c072ad20 feat: add engine_getBlobsV3 method (#20451) 2025-12-17 10:15:49 +00:00
Lorsmirq Benton
6a5b985113 docs: remove orphaned recover CLI documentation (#20447) 2025-12-17 10:13:55 +00:00
joshieDo
1adc6aec00 chore(engine): extract on_persistence_complete (#20443) 2025-12-17 09:07:54 +00:00
Matthias Seitz
5edc16ad85 perf: only populate cache during prewarm (#20445) 2025-12-17 08:46:16 +00:00
phrwlk
f54a8a1ef5 fix(payload): clarify PayloadTransactions mark_invalid semantics (#20452) 2025-12-17 08:44:17 +00:00
leniram159
c681851ec8 chore: make docs correct (#20440)
Co-authored-by: YK <chiayongkang@hotmail.com>
2025-12-17 04:32:18 +00:00
DaniPopes
d964fcbcde chore: simplify execution state providers (#20444) 2025-12-16 22:52:57 +00:00
Alexey Shekhirin
e79691aae7 feat: turn on asm-keccak by default, use maxperf profile in Dockerfiles (#20422) 2025-12-16 22:43:20 +00:00
bigbear
4231f4b688 docs: fix incorrect API example in node-components.mdx (#20297) 2025-12-16 15:09:29 +00:00
Léa Narzis
0b607113dc refactor(era): make era count in era file name optional (#20292) 2025-12-16 15:08:43 +00:00
emmmm
be4dc53b92 docs: fix --color auto option description (#20352) 2025-12-16 15:06:04 +00:00
emmmm
4afb555d06 docs(opstack): document all rollup CLI arguments (#20374) 2025-12-16 15:04:34 +00:00
Matthias Seitz
ab2ef99458 chore: add keccak-global (#20418) 2025-12-16 14:59:09 +00:00
Sophia Raye
bfd4b79245 docs(trace): remove duplicate comment (#20360) 2025-12-16 14:56:01 +00:00
Federico Gimenez
49057b1c0c feat(storage): add with_default_tables() to register RocksDB column families at initialization (#20416) 2025-12-16 12:59:58 +00:00
Gigi
b6772370d7 docs: fix incorrect method reference in try_recover_sealed_with_senders (#20410) 2025-12-16 12:27:53 +00:00
Karl Yu
d72935628a feat: add support for eth/70 eip-7975 (#20255)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-16 12:05:11 +00:00
YK
ad63b135d6 feat(storage): implement EitherWriter/EitherReader methods for RocksDB (#20408) 2025-12-16 11:26:31 +00:00
Brian Picciano
90651ae8e8 feat(engine): Use BAL in state root validation (#20383) 2025-12-16 11:05:51 +00:00
Matthias Seitz
bbd51862d4 chore: rm flaky bench (#20413) 2025-12-16 09:35:38 +00:00
Arsenii Kulikov
08a16a5bde perf: recover transactions in parallel during network import (#20385) 2025-12-16 09:33:24 +00:00
Snezhkko
f2c39db7a2 chore(rpc): fix misleading link and comment (#20367) 2025-12-16 09:32:25 +00:00
oooLowNeoNooo
ae9e84d6e3 fix(discv4): correct ping_interval default value in docs (#20396) 2025-12-16 09:29:45 +00:00
theo
c51da593d1 feat(net/p2p): support fixed external addresses with DNS resolution (#20411) 2025-12-16 09:28:31 +00:00
Matthias Seitz
0e08f9f56c perf: remove unnecessary channels from parallel trie operations (#20406) 2025-12-16 09:15:27 +00:00
sashass1315
7eef092110 docs(exex): sync hello-world notifications loop with code (#20403) 2025-12-16 08:39:45 +00:00
YK
40e8241bf5 feat(storage): use RocksDBBatch in EitherWriter and related modules (#20377) 2025-12-16 03:57:41 +00:00
dependabot[bot]
dd9ff731e4 chore(deps): bump peter-evans/create-pull-request from 7 to 8 (#20402)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 00:11:22 +00:00
dependabot[bot]
83f9d1837f chore(deps): bump actions/download-artifact from 4 to 7 (#20401)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 00:11:00 +00:00
dependabot[bot]
68911e617b chore(deps): bump actions/upload-artifact from 5 to 6 (#20400)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-16 00:10:40 +00:00
0xcharry
36ba6db029 chore: remove redundant .as_str() calls after to_string() (#20404) 2025-12-16 00:10:03 +00:00
Matthias Seitz
fec4432d82 perf: defer transaction pool notifications until after lock release (#20405) 2025-12-15 23:06:34 +00:00
Matthias Seitz
179da26305 perf: use RwLock for transaction pool listeners (#20398)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-12-15 21:47:59 +00:00
Matthias Seitz
b5e7a694d2 chore: update metric once (#20371) 2025-12-15 20:38:24 +00:00
Maxim Evtush
9489667814 fix: post-state generator to include deletions in proptest (#20276) 2025-12-15 16:43:02 +00:00
gustavo
004877ba59 refactor(cli): cleanup repair-trie metrics (#20226) 2025-12-15 16:41:48 +00:00
Brian Picciano
a9e36923e1 feat(trie): Proof Rewrite: Use cached branch nodes (#20075)
Co-authored-by: YK <chiayongkang@hotmail.com>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-12-15 15:27:04 +00:00
DaniPopes
74a3816611 ci: reduce feature powerset depth (#20379) 2025-12-15 14:42:14 +00:00
Alexey Shekhirin
5576d4547f revert: feat(engine): run sync state root if not enough parallelism (#20127) (#20378) 2025-12-15 14:05:54 +00:00
DaniPopes
21216e2f24 perf: use indexed parallel iterators for tx recovery (#20342) 2025-12-15 13:40:03 +00:00
YK
42c1e1afe1 feat(storage): add account history constructors to EitherWriter/EitherReader (#20366) 2025-12-15 12:45:07 +00:00
MoNyAvA
5f7e87fa2a docs: add blob sub-pool to tx pool docs (#20375) 2025-12-15 12:27:54 +00:00
Matthias Seitz
1b417dacc4 chore: sanity check for u64::Max (#20373) 2025-12-15 11:33:50 +00:00
Niven
bb952be5b5 feat(flashblocks): support eth_getBlockTransactionCount for flashblocks (#20291)
Co-authored-by: lucas <66681646+limyeechern@users.noreply.github.com>
Co-authored-by: lucas.lim <lucas.lim@okg.com>
2025-12-15 11:29:23 +00:00
Federico Magnani
f927eec880 chore: export FlashBlockDecoder (#20370) 2025-12-15 11:00:46 +00:00
Tomass
9c61f5568c fix(rpc-testing-util): use buffer_unordered in trace_block_opcode_gas_unordered (#20369) 2025-12-15 10:38:40 +00:00
ligt
662c0486a1 feat(storage): add rocksdb provider into database provider (#20253) 2025-12-15 10:15:57 +00:00
Matthias Seitz
997848c2a1 fix(txpool): remove stale senderinfo (#20368) 2025-12-15 10:00:25 +00:00
Olexandr88
155bdecf3b docs(repo): add Ethereum-specific crates section (#20363) 2025-12-15 09:56:40 +00:00
github-actions[bot]
679234f105 chore(deps): weekly cargo update (#20359)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
2025-12-14 20:54:42 +00:00
phrwlk
419c7b489b fix(rpc): remove dead flashbots module config (#20364) 2025-12-14 20:54:15 +00:00
Rej Ect
06dac07b5f ci(hive): bump actions/cache to v5 (#20349) 2025-12-13 09:04:07 +00:00
YK
5621132b8b feat: add RocksDB variant to EitherReader and EitherWriter (#20288) 2025-12-13 04:06:44 +00:00
Matthias Seitz
3380eb69c8 fix: only collect already tracked accounts (#20341) 2025-12-12 22:09:21 +00:00
Arsenii Kulikov
0366497ada perf: skip redundant recovery (#20343) 2025-12-12 22:01:05 +00:00
Alexey Shekhirin
cd71f3d5a4 feat(engine): record total latencies on instrumented state provider drop (#20337) 2025-12-12 21:14:44 +00:00
Alexey Shekhirin
64909d33e6 feat(engine): cli argument to disable state cache (#20143) 2025-12-12 17:51:22 +00:00
Alexey Shekhirin
3c9ad31344 chore(engine): make InstrumentedStateProvider public (#20335) 2025-12-12 16:41:42 +00:00
gustavo
f3e14fd061 feat(rpc): handle dedicated eth_simulate errors (#20099) 2025-12-12 16:40:13 +00:00
Alexey Shekhirin
daf6b88dc6 feat(node): engine args defaults (#20203) 2025-12-12 15:54:05 +00:00
emmmm
d2d58f9a0e docs: add missing RPC namespaces to JSON-RPC intro (#20321) 2025-12-12 15:40:38 +00:00
Matthias Seitz
ace4e515b5 chore: bump inspectors 0.33.2 (#20334) 2025-12-12 15:39:04 +00:00
Hesham Shabanah
134164954b feat: add --max-peers CLI flag (#20139)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-12 13:26:44 +00:00
Lorsmirq Benton
2775dd1f23 docs: correct comments in custom-inspector (#20304) 2025-12-12 13:21:03 +00:00
Alexey Shekhirin
ac0f9687bd chore(engine): move noisy multiproof debug logs to trace level (#20331) 2025-12-12 13:01:01 +00:00
Arsenii Kulikov
a9c21a395d perf: spawn rpc handlers as blocking (#20330) 2025-12-12 12:15:02 +00:00
Federico Magnani
df7ad9ae45 chore(ethapi): increase visibility tx_batch_sender (#20315)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-12 12:14:43 +00:00
sashass1315
5903e42a98 docs: refresh repo layout crate lists (#20319) 2025-12-12 10:59:57 +00:00
Matthias Seitz
3c41b99599 chore: lower block buffer size (#20324) 2025-12-12 08:15:54 +00:00
pepes
d70d80fff1 fix(docs): document discv5 discovery port 9200 (#20322) 2025-12-12 08:12:08 +00:00
gustavo
ed3a8a03d5 feat(node-core): make rpc server args customizable (#20312) 2025-12-11 23:24:31 +00:00
YK
bfcd46d01d feat: add account_history_in_rocksdb field to StorageSettings (#20282) 2025-12-11 19:37:36 +00:00
Brian Picciano
194d545fae feat(engine): Add BAL stub methods to ExecutionPayload and BlockOrPayload (#20311)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-11 19:07:43 +00:00
sashass1315
97243ec1f4 docs: fix misleading links (#20300) 2025-12-11 18:49:18 +00:00
DaniPopes
93c1b0f52f ci: add more sccache (#20316) 2025-12-11 18:46:11 +00:00
Arsenii Kulikov
474c09095f feat: bump alloy-evm (#20314) 2025-12-11 19:46:34 +01:00
Matthias Seitz
24c298133f feat: allow larger ws frames on client side (#20307) 2025-12-11 16:43:10 +00:00
Block Wizard
da27336a1e docs: add architecture diagrams to ExEx documentation (#20193)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-11 11:41:15 +00:00
Matthias Seitz
2e567d6658 feat: add semaphore for blocking IO requests (#20289) 2025-12-11 11:35:50 +00:00
Alexey Shekhirin
28e7c8a7cb ci: scale down depot runners (#20295) 2025-12-11 11:33:49 +00:00
Matthias Seitz
a2a5e03cb8 perf: fetch header directly (#20294) 2025-12-11 11:18:51 +00:00
Sophia Raye
6073aa5b4a docs(exex): fix DebugApi comment (#20296) 2025-12-11 10:06:31 +00:00
Karl Yu
e90cfedf3d feat: add support for testing_ rpc namespace and testing_buildBlockV1 (#20094)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-11 08:56:46 +00:00
Matthias Seitz
8b27ca6fa2 chore: update engine_getBlobs metric (#20290) 2025-12-11 08:11:54 +00:00
Tomass
1752d6fb99 chore(optimism): move predeploy constant to op-alloy (#20181)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-11 07:04:01 +00:00
emmmm
ac891a780b docs: fix stages order and add missing EraStage (#20283) 2025-12-11 06:26:27 +00:00
Adrian
036626b8a7 docs: improve map_add_ons method documentation (#20248) 2025-12-11 06:03:34 +00:00
josé v
68f0c9812f feat: add transaction_hash_numbers_in_rocksdb field to StorageSettings (#20209) 2025-12-11 01:07:12 +00:00
sashass1315
c9920c9690 docs: clarify network mode, tx gossip and NAT (#20247) 2025-12-10 21:52:04 +00:00
Karl Yu
af82606ff4 feat: add support for debug_getBadBlock (#20177)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-10 21:03:53 +00:00
radik878
38331a362e fix(rpc): avoid signing Optimism deposit transactions (#20254) 2025-12-10 20:46:43 +00:00
Tomass
e8dae2ae7d chore(deps): bump op-alloy to 0.23.0 (#20256)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-12-10 20:44:54 +00:00
Sophia Raye
ce5f90175b docs(jsonrpc): add missing debug namespace RPC methods (#20267) 2025-12-10 17:24:29 +00:00
gustavo
8c361c87c2 feat(txpool): handle more simulated scenarios in test_utils/pool.rs (#20138) 2025-12-10 17:13:59 +00:00
Block Wizard
4fbbb1fe54 feat: add recover_transactions_unchecked_ref to BlockBody (#20266) 2025-12-10 17:13:08 +00:00
Brian Picciano
b7d8815104 perf(prune): use delete_current_duplicates for MerkleChangeSets tables (#20230) 2025-12-10 13:33:11 +00:00
Alexey Shekhirin
b91cd8f451 ci: sccache (#20265) 2025-12-10 13:05:25 +00:00
Alexey Shekhirin
09aee4e35a ci: use 16 cores for Hive workflow (#20264) 2025-12-10 13:02:14 +00:00
Alexey Shekhirin
505a384b10 ci: increase partitions for crate-checks to 3 (#20261) 2025-12-10 13:02:11 +00:00
phrwlk
6e00b99b67 docs: use canonical --rollup.sequencer and note aliases (#20260) 2025-12-10 12:18:36 +00:00
emmmm
1d389cfe7a docs(jsonrpc): add missing debug namespace RPC methods (#20258) 2025-12-10 12:17:50 +00:00
Matthias Seitz
2e62387469 feat: use max retries for debug consensus rpc client (#20257) 2025-12-10 11:06:38 +00:00
Block Wizard
31133255fe docs(reth-bench): fix incorrect authrpc.jwtsecret flag (#20249) 2025-12-10 09:30:50 +00:00
Matthias Seitz
a6b9472d1c fix: use generic header (#20250) 2025-12-10 09:11:39 +00:00
forkfury
6636d2a2ad docs: fix timestamp validation comment (#20246) 2025-12-10 08:41:23 +00:00
YK
ab6854d159 docs(reth-bench): fix incorrect output flag in README (#20240) 2025-12-10 07:18:34 +00:00
Charlie-Mack
5a274fc939 feat: add example for launching a node with custom rpc middleware (#20159) 2025-12-10 07:15:46 +00:00
radik878
c9431b224b refactor(rpc): remove dead got_notif flag from RpcService batch handler (#20171) 2025-12-10 07:15:09 +00:00
emmmm
8cbfd91db0 docs: add missing bodies_history and merkle_changesets prune config fields (#20244) 2025-12-10 07:10:57 +00:00
Block Wizard
43f9942ba7 docs(txpool): fix PoolSize total field comment to include blob pool (#20241) 2025-12-10 07:05:42 +00:00
Léa Narzis
06adc3ee0c refactor(rpc): return error instead of clamping for get_filter_block_range (#20218) 2025-12-10 07:03:30 +00:00
dependabot[bot]
fbf6be4cf2 chore(deps): bump dawidd6/action-homebrew-bump-formula from 6 to 7 (#20205)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-10 07:01:28 +00:00
Forostovec
21d61d40d1 docs: document state and block overrides for trace_call (#20217) 2025-12-10 07:00:59 +00:00
YK
cf7d709358 perf(engine): batch multiproof messages (#20066)
Co-authored-by: 0xSooki <0xsooki@gmail.com>
2025-12-10 03:42:08 +00:00
Vitalyr
e9355caba5 feat(reth-bench-compare): add reth command to summary output (#20089) 2025-12-10 02:12:57 +00:00
Brian Picciano
fdd9d5bb40 docs(trie): correct TrieInput::extend_with_blocks docstring (#20225) 2025-12-10 02:03:42 +00:00
AJStonewee
9eeba7e6b3 feat(transaction-pool): add new_blob_pool_transactions_listener (#20216)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-12-09 23:41:00 +00:00
forkfury
0085acc868 docs: remove incorrect total_difficulty mention from process_iter (#20234)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-12-09 23:27:16 +00:00
Alexey Shekhirin
c697147f90 ci: use depot runners (#20222) 2025-12-09 23:03:44 +00:00
kurahin
7388d6636d docs(config): clarify PruneConfig::merge semantics (#20235) 2025-12-09 21:15:02 +00:00
SashaMalysehko
0b859c0735 fix(rpc): validate fee history reward percentiles (#20198) 2025-12-09 21:03:17 +00:00
yyhrnk
a8e0606fa7 fix(cli): reference correct --without-evm flag in init-state error (#20231) 2025-12-09 21:00:45 +00:00
Galoretka
969689d9b6 docs: add admin_peers and admin_clearTxpool sections (#20185) 2025-12-09 20:59:44 +00:00
Adrian
ad2081493a docs: add missing documentation for serde_bincode_compat::ExExNotification (#20236) 2025-12-09 20:59:05 +00:00
Brian Picciano
abfb6d3965 feat(cli): Allow walking a range of an MDBX table using db mdbx get (#20233) 2025-12-09 20:37:06 +00:00
Alexey Shekhirin
0f0eb7a531 feat(net): pool transactions import duration metric (#20228) 2025-12-09 13:57:01 +00:00
Alexey Shekhirin
4f1e486b4f feat(engine): execution wait, pre, post metrics (#20166) 2025-12-09 13:30:58 +00:00
Alexey Shekhirin
05307d088c perf(chain-state): executed_block_receipts_ref (#20227) 2025-12-09 13:08:15 +00:00
Arsenii Kulikov
245cca7ce2 perf: avoid collect in truncate_pool (#20221) 2025-12-09 11:08:21 +00:00
Arsenii Kulikov
28d6996fc4 feat: add helper method to eth validator (#20206) 2025-12-08 22:48:54 +00:00
Karl Yu
0eaffdf489 feat: add StorageSettings for StoragesHistory in RocksDB (#20154) 2025-12-08 22:22:36 +00:00
futreall
9c141cac4b fix(rpc): return error if toBlock exceeds current head (#20202) 2025-12-08 17:42:01 +00:00
Léa Narzis
fc6ab35c5c test(era): complete int tests with roundtrip mainnet era files (#20064) 2025-12-08 17:01:21 +00:00
joshieDo
f88bf4e427 fix: set merkle changesets distance minimum to 128 (#20200) 2025-12-08 16:10:11 +00:00
Matthias Seitz
3d330caf36 perf: avoid duplicate storage get call (#20180) 2025-12-08 16:02:22 +00:00
Matthias Seitz
5a43e77771 fix: trace filter range off by one (#20199) 2025-12-08 15:54:08 +00:00
forkfury
5b3c479ed5 feat(primitives-traits): add recover_transactions_ref to avoid cloning (#20187)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-08 14:51:07 +00:00
Matthias Seitz
dc06b47abe fix: make inserted blocks part of fcu canonical (#20164)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-12-08 14:06:39 +00:00
Arsenii Kulikov
e9cd7cc003 feat: parallelize recovery (#20169)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-08 14:05:37 +00:00
Alexey Shekhirin
f633efc969 ci: run on ubuntu instead of reth runner (#20196) 2025-12-08 14:30:20 +01:00
github-actions[bot]
2f55b1c30f chore(deps): weekly cargo update (#20174)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
2025-12-07 11:15:14 +00:00
Matthias Seitz
3e96557e69 chore: featuer gate rocksdb (#20170) 2025-12-06 18:55:55 +00:00
sashass1315
8bd970bad8 fix: make get_exact error on missing blobs to match trait contract (#19347)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-06 17:32:19 +00:00
Fallengirl
d765521c3d chore(deps): clean up unused imports and dependencies in reth-downloaders (#19875) 2025-12-06 16:27:27 +00:00
Charlie-Mack
4bde1bb048 fix(node): Added missing trait bound on RethRpcAddOns for EthereumAddOns (#20155) 2025-12-06 16:20:35 +00:00
Tomass
9146ca4501 chore(optimism): deduplicate ADDRESS_L2_TO_L1_MESSAGE_PASSER (#20160) 2025-12-06 14:28:38 +00:00
sashass1315
6655a9377a docs: add architecture diagram for EVM node component (#20162) 2025-12-06 13:35:45 +00:00
stevencartavia
56e60a3704 feat: add merge_if_module_configured_with that accepts a closure (#20158) 2025-12-06 10:49:47 +00:00
cui
193af2219b feat: generate part of sidebar (#20040) 2025-12-06 10:16:09 +00:00
Léa Narzis
73b4fcc41a docs(era): use lighthouse decoding support for era types (#20147) 2025-12-06 10:15:16 +00:00
GarmashAlex
6fc752d66a docs: fix broken link (#20076) 2025-12-06 10:13:46 +00:00
Bashmunta
ed104a964a refactor(net): remove unused enable_packet_filter and ban_duration from config (#20146) 2025-12-06 08:02:44 +00:00
ligt
00ccb2b9b4 feat(persistence): implement RocksDB provider (#20071)
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
2025-12-05 20:29:19 +00:00
Matthias Seitz
addb51f2e2 chore: use drop guard for transact task (#20134) 2025-12-05 18:18:55 +00:00
Brian Picciano
a80c290ca0 feat(cli): repair-trie metrics (#20121) 2025-12-05 16:10:43 +00:00
Alexey Shekhirin
3830c765e0 feat(node): tx pool optional args defaults (#20142) 2025-12-05 15:55:18 +00:00
Matthias Seitz
190297083a chore: relax server impl (#20141) 2025-12-05 15:21:58 +00:00
Matthias Seitz
9712fe56e5 chore: add identifying info to traces (#20140) 2025-12-05 14:57:43 +00:00
Alexey Shekhirin
0a6d20bd1b feat(node): tx pool args defaults (#20136) 2025-12-05 13:27:25 +00:00
Arsenii Kulikov
4c17de8553 fix: add missing 2718 impl for receipt (#20137) 2025-12-05 12:49:49 +00:00
Matthias Seitz
a0dc85fc95 chore: bump revm inspectors (#20132) 2025-12-05 11:44:39 +00:00
Merkel Tranjes
3c1fc99600 perf(rpc): use maybe_cached_block_and_receipts for AtBlockHash (#19910)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-05 11:31:43 +00:00
Fallengirl
49059f500e fix(rpc): remove redundant blob_gas_used calculation in eth_callBundle (#20131) 2025-12-05 11:30:28 +00:00
Alexey Shekhirin
f744ad6e0b feat(engine): more engine execution tracing spans (#20135) 2025-12-05 11:16:38 +00:00
Alexey Shekhirin
cfde951976 feat(engine): run sync state root if not enough parallelism (#20127) 2025-12-04 22:59:00 +00:00
Arsenii Kulikov
07bca4f26a perf(engine): only recover senders once (#20118) 2025-12-04 20:43:51 +00:00
Arsenii Kulikov
9e1b2474b0 fix: change Receipt rlp (#20074) 2025-12-04 20:29:22 +00:00
Matthias Seitz
d71cd129a7 chore: allow empty blobparams in ethconfig (#20105) 2025-12-04 18:48:37 +00:00
Ignacio Hagopian
e53990cf41 fix(chainspec): add ChainConfig to StatelessInput and add ChainConfig creator helpers (#20101)
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
2025-12-04 18:46:04 +00:00
Matthias Seitz
61f5b4e06f chore: add payload info to trace (#20124) 2025-12-04 17:42:09 +00:00
Matthias Seitz
1f7d552d63 test: ensure invalid block hook is opt out (#20125) 2025-12-04 17:38:54 +00:00
gustavo
b8f0b5d67e fix(rpc): improve/preserve error messages for TransactionConversionError (#20057) 2025-12-04 15:43:42 +00:00
Ignacio Hagopian
409b74fc36 feat(tests): add Osaka fork specification to ForkSpec enum (#20120) 2025-12-04 14:33:38 +00:00
0xMushow
282932d3eb fix(net): enforce stricter bound on check_peer_blocks (#20116) 2025-12-04 11:50:36 +00:00
Block Wizard
b6d886f288 docs(engine): fix transact_batch comment (#20097) 2025-12-04 11:44:08 +00:00
joshieDo
d6caf7f063 fix: set minimum pruning distance to 64 blocks for trie changesets (#20108) 2025-12-04 10:12:38 +00:00
gustavo
7b90bcd3e1 chore(docker): upgrade lighthouse image to version 8.0.1 (#20109) 2025-12-03 22:55:59 +00:00
Bashmunta
a68980dd72 fix(txpool): derive accurate queued reason for SubPool::Blob (#20095) 2025-12-03 22:03:37 +00:00
Matthias Seitz
4adf163fdc chore: add alias for flashblocks-url (#20093) 2025-12-03 20:17:34 +00:00
Matthias Seitz
7e6a59b6ac chore: make ethconfig work with headermut (#20102) 2025-12-03 17:16:18 +00:00
Matthias Seitz
0b3fc3019f chore: add helper fn for building pool (#20100) 2025-12-03 16:02:36 +00:00
Đạt Nguyễn
3a9dbdc840 feat(tx-pool): make metrics, listener structs, and fields public (#20087) 2025-12-03 10:50:50 +00:00
gustavo
af1e12fd43 chore(txpool): feature gate test (#20082) 2025-12-03 10:04:07 +00:00
Block Wizard
8fd86ba516 docs(metrics): fix mpsc copy-paste doc errors (#20085) 2025-12-03 10:03:40 +00:00
YK
e0a6f54b42 perf(trie): add HashedPostStateSorted::from_reverts (#20047) 2025-12-03 05:05:23 +00:00
Bashmunta
98e9a1d09e fix(ipc): make IpcServer Debug impl generic (#20042) 2025-12-02 15:08:02 +00:00
joshieDo
8f2811dd19 fix: ensure MerkleChangeSets pruner only runs if pipeline stage has finished (#20073) 2025-12-02 14:08:47 +00:00
dependabot[bot]
9260f2fe40 chore(deps): bump actions/upload-artifact from 4 to 5 (#20063)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-12-02 13:58:49 +00:00
Block Wizard
cefddbb39d docs(engine): fix canonical_block_by_hash comments (#20067) 2025-12-02 11:06:40 +00:00
strmfos
8f5b4ae324 fix(rpc): check correct variable for division by zero in blob gas ratio (#20053) 2025-12-01 11:57:51 +00:00
Block Wizard
cdb896f8de docs(trace): fix trace_callMany params structure and formatting inconsistencies (#20051) 2025-12-01 10:25:20 +00:00
github-actions[bot]
4f5b842543 chore(deps): weekly cargo update (#20043)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
2025-11-30 10:06:57 +00:00
Arsenii Kulikov
b3c00ed602 fix: convert headers in newHeads (#20036) 2025-11-28 20:27:08 +00:00
Alexey Shekhirin
7922edf63d feat(storage): log storage settings on startup (#19931) 2025-11-28 20:01:59 +00:00
Matthias Seitz
93d81ed4d5 chore: add elapsed info to logs (#20035) 2025-11-28 19:42:48 +00:00
Galoretka
0334953357 docs: correct profiling feature name and jemalloc env var (#20030) 2025-11-28 16:34:41 +00:00
Francis Li
194a01adda feat(engine): Update execution cache on inserted executed blocks (#19822)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-28 14:32:15 +00:00
oxBoni
c5764f51bd chore: avoid cloning block range when ranking peers (#20033)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-28 14:12:44 +00:00
dependabot[bot]
6becc6bd60 chore(deps): bump dawidd6/action-homebrew-bump-formula from 5 to 6 (#19951)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-28 14:04:50 +00:00
ANtutov
56cb1581d6 chore(ethereum): avoid cloning requests in try_into_v4 (#19851) 2025-11-28 14:00:54 +00:00
gustavo
c6b1a45ce4 feat(txpool): add append_* helpers (#20028) 2025-11-28 13:59:43 +00:00
Vitalyr
b92741a1db refactor(tree): remove unnecessary block clone (#19848) 2025-11-28 13:57:52 +00:00
cui
ef0c1e2d50 fix: update sidebar.ts (#20032)
Co-authored-by: weixie.cui <weixie.cui@okg.com>
2025-11-28 13:10:21 +00:00
Matthias Seitz
44ba8eb640 chore: drop jar early (#20031) 2025-11-28 12:44:33 +00:00
Matthias Seitz
507becb451 docs: fix trace call docs (#20029) 2025-11-28 13:10:56 +01:00
cui
e910e58778 feat: docs for op-reth (#20024)
Co-authored-by: weixie.cui <weixie.cui@okg.com>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-28 11:41:27 +00:00
radik878
47e8f51627 fix(transaction-pool): Spawn ValidationTask to keep channel open (#19943)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-28 11:14:57 +00:00
ANtutov
813786247a docs(trace): fix trace_call method invocation table (#19977)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-28 11:12:22 +00:00
Bashmunta
c9e661658b perf: disable storage branch masks for non-target accounts (#19598) 2025-11-28 11:11:55 +00:00
sashass1315
2695f00b83 fix: propagate-only pending listener dropping promoted txs (#20013) 2025-11-28 10:09:28 +00:00
ANtutov
73117ecd0a fix(rpc): unify EthFilterConfig TTL default with constant (#20026) 2025-11-28 09:57:07 +00:00
Fibonacci747
b2fe49efd9 fix: remove dead resolved_links state from SyncTree and its usage (#20022) 2025-11-28 09:55:23 +00:00
Matthias Seitz
6446f404ff test: add helper for tracing a range of blocks (#19959) 2025-11-28 11:01:19 +01:00
Matthias Seitz
846ffc6f7e chore: consume result logs (#20025) 2025-11-28 10:53:18 +01:00
Tomass
36c62807dc fix(mev): correct refund calculation in sim_bundle (#19991) 2025-11-28 09:20:32 +00:00
YK
e8fd2eedfd perf(trie): compute and sort trie inputs async (#19894) 2025-11-28 06:10:59 +00:00
Matthias Seitz
65d75a77c0 fix(net): back off slightly after graceful connection termination (#20020) 2025-11-27 23:41:09 +00:00
phrwlk
ac6069e1e0 docs(jsonrpc/trace): document opcode gas endpoints (#20011) 2025-11-27 22:09:33 +00:00
Matthias Seitz
8621308952 fix: keep peer status unchanged on new discovered updates (#20018) 2025-11-27 19:37:53 +00:00
YK
6598b88e02 fix(bench-compare): filter empty strings from additional reth args (#20004) 2025-11-27 18:07:55 +00:00
Matthias Seitz
78f8dddfc2 chore: add additional peer manager logs (#20015) 2025-11-27 17:01:26 +00:00
Artyom Bakhtin
014f115c47 feat: Reproducible builds and *.deb packages (#19678)
Signed-off-by: bakhtin <a@bakhtin.net>
2025-11-27 11:35:22 +00:00
stevencartavia
07c5956ce7 chore: move CliHeader to primitives traits and rename to HeaderMut (#20001) 2025-11-27 09:43:55 +00:00
oxBoni
d3f6c4c666 chore: remove unused stream item generic from SyncListener future impl (#20003) 2025-11-27 09:43:47 +00:00
Fibonacci747
0f4d475223 fix: preserve basic nodes from peers file by avoiding peer_config overwrite (#19887)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-26 23:08:00 +00:00
gustavo
c037a57d05 perf(payload): move fields instead of cloning in PayloadBuilderStack::try_build (#19998) 2025-11-26 22:03:54 +00:00
Galoretka
1e4b15b6cc docs: document missing config sections and fields (#19996) 2025-11-26 21:58:39 +00:00
josé v
eab90f8fe6 chore: refactor pooled_transaction_hashes_max for early termination (#19999) 2025-11-26 21:56:29 +00:00
Matthias Seitz
8aad5a6006 chore: rm unused warnings (#19958) 2025-11-26 23:05:41 +01:00
Snezhkko
f386f96f1e perf(payload): move parent_header in PayloadBuilderStack::build_empty_payload (#19995) 2025-11-26 19:13:29 +00:00
Matthias Seitz
dc8c4eebdc feat: add helper for is nonce too low (#19993) 2025-11-26 19:12:35 +00:00
Matthias Seitz
5b9d8c5008 chore: add clone into pooled (#19989) 2025-11-26 15:38:02 +00:00
Brian Picciano
7345e1e5b5 feat(trie): Benchmarks comparing proof_v2 to "legacy" proof implementation (#19967)
Co-authored-by: YK <chiayongkang@hotmail.com>
2025-11-26 14:18:15 +00:00
stevencartavia
ac120ffd31 feat: introduce PayloadValidator::payload_to_block (#19953) 2025-11-26 11:26:57 +00:00
Matthias Seitz
2f58f67974 feat!: accept error by ref (#19981) 2025-11-26 10:04:20 +00:00
cui
c72644f867 fix: docs link in https://reth.rs/cli/cli (#19961) 2025-11-26 09:47:24 +00:00
Rej Ect
e62cb8f82b ci: update checkout action to v6 (#19930) 2025-11-26 09:46:58 +00:00
Matthias Seitz
0f11d469bb chore: extract notify event helpers (#19985) 2025-11-26 09:25:04 +00:00
Matthias Seitz
21a4b13828 feat: add next_tx_and_priority for Besttransactions (#19982) 2025-11-26 09:11:45 +00:00
Arsenii Kulikov
8528769896 fix: better RpcConvert bounds (#19980) 2025-11-25 23:17:11 +00:00
Matthias Seitz
acf2d948b6 chore: more txpool pub (#19978) 2025-11-25 20:52:42 +00:00
Matthias Seitz
587b1f45db chore: add helper for Chain (#19976) 2025-11-25 18:13:43 +00:00
Matthias Seitz
69b97e3f63 chore: make more pool internals pub (#19974) 2025-11-25 17:43:37 +00:00
Matthias Seitz
cb932b3f7d feat: add helper for TransactionValidationOutcome (#19973) 2025-11-25 17:19:41 +00:00
Matthias Seitz
52defdc479 feat: export validator (#19971) 2025-11-25 16:42:22 +00:00
Bashmunta
a3ee6b2761 chore: avoid redundant factory construction in witness/proof overlay init (#19969) 2025-11-25 16:25:24 +00:00
Matthias Seitz
8b38877797 chore: make fields pub (#19970) 2025-11-25 15:36:19 +00:00
Arsenii Kulikov
70b8724b8d feat: PoolTransaction::requres_nonce_check (#19968) 2025-11-25 14:40:58 +00:00
Matthias Seitz
d9d833f556 feat: make a few more things pub (#19966) 2025-11-25 14:20:50 +00:00
gustavo
edc31d23e2 feat(txpool): add total_other_transactions metric (#19965) 2025-11-25 13:59:30 +00:00
strmfos
87e2716f3f fix(net): correct Debug impl for StatusEth69 (#19963) 2025-11-25 11:58:02 +00:00
Brian Picciano
1b59cd2155 feat(trie): Proof V2: retain proof nodes which match targets (#19941)
Co-authored-by: YK <chiayongkang@hotmail.com>
2025-11-25 11:23:27 +00:00
YK
1c31abce27 fix(metrics): remove duplicate trie_input_duration recording (#19955) 2025-11-25 10:25:40 +00:00
emmmm
8c8efc6082 docs(consensus): fix misleading docstring in validate_4844_header_standalone (#19960) 2025-11-25 08:54:43 +00:00
Forostovec
c680d2e7bd docs: fix incorrect default values in configuration.mdx (#19936) 2025-11-25 08:33:44 +00:00
Dan Cline
a7bd7bd626 feat(cli): add db account-storage command (#19952) 2025-11-25 08:32:32 +00:00
Francis Li
ba862da221 feat(flashblock): Enable eth_getTransactionByHash support for flashblock (#19954)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-25 08:31:24 +00:00
Dan Cline
e57fe4510c docs(cli): remove reference to engine experimental (#19944) 2025-11-24 22:47:49 +00:00
Vitalyr
f2600ace51 chore: avoid needless clone in module array conversion (#19939) 2025-11-24 21:50:17 +00:00
Matthias Seitz
366f509b2f chore: spawn tx iter earlier (#19948) 2025-11-24 21:22:59 +00:00
Francis Li
118fd3b372 feat(flashblocks): Cache recent flashblocks (#19786)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-24 18:01:46 +00:00
cui
b546aca5c1 fix: npm run build failed in docs/vocs directory (#19937) 2025-11-24 16:35:38 +00:00
Acat
c7b689016a fix(txpool): ensure skipped high-priority transactions are tracked in BestTransactions (#19940) 2025-11-24 15:31:47 +00:00
Arsenii Kulikov
4467bc9f4b fix: correctly fetch pending hashes (#19938) 2025-11-24 14:36:43 +00:00
YK
c2d73988de chore(test): Increase timeout for ethereum e2e tests (#19933) 2025-11-24 11:30:30 +00:00
Arsenii Kulikov
c2912a7333 refactor(tracing): introduce DebugInspector (#19925) 2025-11-24 10:38:13 +00:00
David Klank
bd9e41c551 chore(era): move reth-ethereum-primitives to dev-dependencies (#19847) 2025-11-24 09:56:30 +00:00
cui
b6f62473e0 feat: update docs link in cli/SUMMARY.mdx (#19909) 2025-11-24 09:56:02 +00:00
Bashmunta
d429a665b2 chore: remove unused lifetime from map_internal_err impl (#19924) 2025-11-24 09:52:39 +00:00
Andrés David Ramírez Chiquillo
75af47c456 feat(payload): enforce Osaka validation for GetPayloadV4 (#19929) 2025-11-24 09:51:12 +00:00
YK
f1fc979116 feat(reth-bench-compare): add standard deviation metrics to comparison report (#19928) 2025-11-24 09:39:27 +00:00
Maximilian Hubert
d278b75c3a chore(stages): fix naming and simplify add_stages implementation (#19923) 2025-11-24 08:47:54 +00:00
Đạt Nguyễn
e03c9da85c refactor: remove unused add_transactions_with_origins trait (#19824) 2025-11-24 06:53:10 +00:00
Arsenii Kulikov
ee63c7d6b4 refactor: simplify rpc state provider traits (#19920) 2025-11-23 19:06:10 +00:00
Matthias Seitz
1702107028 feat: make txpool notify fns pub (#19918) 2025-11-23 14:07:59 +01:00
github-actions[bot]
32f0a74462 chore(deps): weekly cargo update (#19917)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
2025-11-23 11:50:58 +00:00
Arsenii Kulikov
a83ac8cc63 refactor(e2e): relax bounds (#19913) 2025-11-23 13:05:48 +01:00
Léa Narzis
7f40013cf6 feat(tracing-otlp): make trace id ratio sample customizable with --tracing-otlp.sample-ratio arg (#19438) 2025-11-21 21:05:43 +00:00
Arsenii Kulikov
6e365949c4 feat: allow customizing dev block timestamp (#19904) 2025-11-21 20:38:20 +00:00
Léa Narzis
e15b404a30 feat(era-file): back to era file support (#19482) 2025-11-21 20:03:28 +00:00
Alexey Shekhirin
39ef6216fb feat(provider, static-file): transaction senders segment (#19508)
Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com>
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-11-21 19:34:16 +00:00
Francis Li
5e0732404c chore(op-alloy): update op-alloy to v0.22.4 (#19905) 2025-11-21 18:25:05 +00:00
gustavo
9f3949cd35 chore(examples): complete state_provider_example (#19903) 2025-11-21 15:58:49 +00:00
Mablr
b0494a158a chore(rpc-eth-types): use FillTransaction from alloy (#19890) 2025-11-21 15:52:28 +00:00
YK
002e755dd4 chore(bench-compare): Add latency distribution stats to reth-bench-compare (#19873)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-21 14:52:55 +00:00
Arsenii Kulikov
a43128277f fix: correctly poll tx fetcher (#19900) 2025-11-21 14:41:28 +00:00
Alexey Shekhirin
86825ac3b7 refactor(cli): deduplicate ethereum init_tracing implementations (#19898) 2025-11-21 14:23:57 +00:00
Alexey Shekhirin
0ba122923a ci: partition cargo-checks job (#19897) 2025-11-21 12:48:31 +00:00
Dan Cline
2a953a821a fix: remove noisy storage proof spans (#19892) 2025-11-21 11:48:32 +00:00
Matthias Seitz
cc7edeb354 chore: dont treat invalid fork as fatal (#19888)
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
2025-11-20 20:51:49 +01:00
gejeduck
9cdcc8e087 feat: respect BlockRangeInfo when selecting peer for request (#16704)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-20 18:19:11 +00:00
Merkel Tranjes
55dacfc739 chore: bump op-alloy deps to 0.22.3 (#19885) 2025-11-20 18:18:53 +00:00
stevencartavia
1ca4348db7 chore: replace op-reth OpReceipt with op-alloy's (#19846)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-11-20 15:59:21 +00:00
Léa Narzis
8c2b037c9a fix(era): fix roundtrip, decoding receipt for era mainnet test (#19862) 2025-11-20 14:49:01 +00:00
Brian Picciano
b72bb6790a feat(trie): Proof rewrite: implement stack-based algorithm for calculating trie nodes from leaves (#19863)
Co-authored-by: YK <chiayongkang@hotmail.com>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-20 13:37:55 +00:00
Arsenii Kulikov
c48aed264c refactor: tx policy traits (#19878) 2025-11-20 13:33:46 +00:00
gustavo
d550e4eb07 test(provider): remove stale todo (#19876) 2025-11-20 12:21:51 +00:00
Brian Picciano
c195aee2b2 feat(trie): Implement set_hashed_address on mock trie cursors (#19864) 2025-11-20 11:53:22 +00:00
Matthias Seitz
2e5a155b6d chore: use installed client name for start log (#19868) 2025-11-20 01:58:53 +00:00
Matthias Seitz
5b6ce8bd64 fix: ensure we poll again (#19866) 2025-11-20 00:11:32 +00:00
sashass1315
c75dc322d9 feat(rpc): warn when --ws.api is set but --ws is disabled (#19855) 2025-11-19 23:57:44 +00:00
Alexey Shekhirin
cfc34367fb feat(provider): change storage settings on existing nodes via CLI (#19771)
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
2025-11-19 22:47:28 +00:00
Alexey Shekhirin
17985b2c94 chore(engine, trie): cleanup traces (#19856) 2025-11-19 17:46:36 +00:00
Brian Picciano
c57792cff4 feat(trie): Implement skeleton of proof_v2 (#19687) 2025-11-19 16:33:05 +00:00
YK
e58aa09f82 perf(engine): return sorted data from compute_trie_input (#19340) 2025-11-19 16:01:24 +00:00
Xzavier
a72c1dab88 fix(net): resolve external ip on startup (#19852) 2025-11-19 15:50:14 +00:00
Dan Cline
65f72e3209 chore(trie): add spans for how long we wait on proofs in recv (#19859) 2025-11-19 15:49:35 +00:00
Alexey Shekhirin
819330c596 feat(engine): new payload last gas/s metric (#19853) 2025-11-19 12:43:34 +00:00
joshieDo
e93bd0a087 fix: ensure that pruning all rows doesn't leave the offset file corrupted (#19819) 2025-11-18 22:07:23 +00:00
joshieDo
d10070e6f4 fix: stage drop command shouldnt delete jars (#19817) 2025-11-18 22:07:18 +00:00
Dan Cline
a301276e4b feat(primitives-traits): add teragas (#19843) 2025-11-18 22:04:50 +00:00
Arsenii Kulikov
27d28e5e7d feat: EthApiError::from_revert (#19836) 2025-11-18 19:51:26 +00:00
Đạt Nguyễn
7a72550745 perf(rpc): reduce estimate gas trait bounds to EvmStateProvider (#19746) 2025-11-18 18:54:37 +00:00
strmfos
a812aea8d1 fix: correct argument order in save_diff calls for invalid block hooks (#19676) 2025-11-18 18:36:03 +00:00
joshieDo
982fa4829a chore: add logs to StaticFileProvider::check_consistency (#19816) 2025-11-18 18:31:22 +00:00
Forostovec
4836062d7b feat(node): remove unnecessary ConnWrapper clone in connect() (#19456) 2025-11-18 18:07:07 +00:00
Alexey Shekhirin
9dc6e256a9 feat(trie): record cursor metrics as span fields (#19830) 2025-11-18 15:15:13 +00:00
Bashmunta
02fc6af313 fix(rpc): metered getPayloadBodiesByHash timing to await before recording (#19827) 2025-11-18 15:12:04 +00:00
gustavo
90e265134f chore: document filter topic usage in db-access example (#19829) 2025-11-18 14:56:33 +00:00
Karl Yu
4f94fa240f feat: add helpers for testing rpc requests with prestate (#19790)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-18 15:34:34 +01:00
Matthias Seitz
69c219eede chore: bump version v1.9.3 (#19831) 2025-11-18 14:59:51 +01:00
joshieDo
9501b4b55a fix: ensure receipt consistency check is done over block number (#19723) 2025-11-18 11:00:57 +00:00
radik878
d997bd0634 docs(db): refresh db crate docs to current API and paths (#19818) 2025-11-17 23:55:40 +00:00
ANtutov
8020cf4494 fix(era-downloader): align checksums with file index in fs::read_dir (#19793)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-17 22:27:16 +00:00
Matthias Seitz
d726375d11 chore: remove flaky benches (#19804) 2025-11-17 22:27:10 +00:00
Matthias Seitz
e634dc46c8 fix: remove bad reset and cancel on drop (#19821) 2025-11-17 22:26:43 +00:00
Matthias Seitz
940be8a092 fix(net): use external ip for discv5 config (#19784) 2025-11-17 22:26:19 +01:00
Dan Cline
23eb96c209 fix(cli): always commit the unwind for stage run headers (#19768) 2025-11-17 19:26:46 +00:00
Matthias Seitz
2a16222ea1 revert: "perf(persistence): improve write batch for HashedPostState & TrieUpdatesSorted" (#19814) 2025-11-17 20:39:01 +01:00
Alexey Shekhirin
fb763edb43 refactor(provider): unify static file indexes into one struct (#19803) 2025-11-17 19:02:28 +00:00
Alexey Shekhirin
6fa11ff2f8 test(prune): fix bodies static files expected block range (#19811) 2025-11-17 17:45:59 +00:00
phrwlk
1568f4c451 perf(payload): remove string allocations and unused import (#19799) 2025-11-17 17:10:13 +00:00
YK
d5025392d0 perf(trie): optimize chunking configuration (#19800)
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
2025-11-17 15:47:24 +00:00
Forostovec
90621de27c fix(prune): avoid extra iterator consumption (#19758)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-17 15:43:47 +00:00
gustavo
adbc68c66c fix: add alloy-rlp dependency to testing-utils (test_sign_eip_155) (#19807) 2025-11-17 15:41:50 +00:00
joshieDo
cac2443dfc chore: add logs around commit and update_index on StaticFileProvider (#19802)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-17 14:51:11 +00:00
Andrés David Ramírez Chiquillo
ea794ff387 chore(provider): use parking_lot RwLock for overlay cache (#19798) 2025-11-17 14:40:49 +00:00
Matthias Seitz
4efd3f8882 chore(op-reth/scr): update superchain-registry (#19806)
Co-authored-by: theo <80177219+theochap@users.noreply.github.com>
2025-11-17 15:44:16 +01:00
YK
c5365de1fb chore(bench-compare): clarify percentage calculation method for final report (#19796) 2025-11-17 13:11:44 +00:00
Ligt Nguyen
65ca4a3afa perf(persistence): improve write batch for HashedPostState & TrieUpdatesSorted (#19739) 2025-11-17 12:56:41 +00:00
gustavo
4260ccd2b5 chore: remove unnecessary todos in witness_db (#19801) 2025-11-17 10:57:17 +00:00
YK
ab5c4452c1 chore: add code owners for reth-bench-compare (#19797) 2025-11-17 00:28:34 +00:00
Vaibhav Arora
af2cc34c36 docs: update rust version (#19789) 2025-11-16 23:59:19 +00:00
Merkel Tranjes
74d9e8b73a chore: inline hardcoded filter name in segment filename (#19420) 2025-11-16 23:57:59 +00:00
Forostovec
48624dcd8c fix(net): fix update_root ENR/LINK handling (#19620) 2025-11-16 23:55:53 +00:00
Fibonacci747
55875ec8a4 fix(transaction-pool): mark TransactionEvent::Invalid as final (#19782) 2025-11-16 23:43:52 +00:00
Léa Narzis
d66069deb0 feat(rpc-provider): add MeteredBatchRequests(Future) (#19779) 2025-11-16 23:26:53 +00:00
Gengar
d1084ca43a docs: clarify BetterPayloadEmitter emits both Better and Freeze outcomes (#19795) 2025-11-16 23:24:19 +00:00
forkfury
46aa02876d fix(tasks): prevent infinite recursion in TaskSpawner implementation (#19788) 2025-11-16 23:22:14 +00:00
sashass1315
2ade18d111 chore(payload): remove redundant attr clone (#19791) 2025-11-16 10:24:50 +00:00
Francis Li
5866a82516 feat(flashblock): improve state root calculation condition (#19667) 2025-11-16 10:22:59 +00:00
github-actions[bot]
a3cebced10 chore(deps): weekly cargo update (#19785)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
2025-11-16 07:17:05 +00:00
Block Wizard
fce0825f81 fix(grafana): restore DS_PROMETHEUS placeholder replacement (#19773) 2025-11-15 09:30:53 +00:00
Jennifer
7fdd9c39e3 refactor: rename state_root span to await_state_root (#19769) 2025-11-14 22:56:44 +00:00
Alexey Shekhirin
860a453930 refactor(cli): db subcommands (#19754) 2025-11-14 22:37:01 +00:00
Brian Picciano
f88fae0ea1 perf(trie): Cache overlays in the OverlayStateProviderFactory (#19752) 2025-11-14 22:20:58 +00:00
Avory
60f663e5b4 fix(payload): emit events for Freeze payload outcomes (#19435)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-14 17:13:39 +00:00
Alexey Shekhirin
c78bca259e chore: replace labels in Grafana dashboard for docker compose (#19756) 2025-11-14 16:35:08 +00:00
Alex Pikme
9eff492d48 feat(flashblocks): add metrics for current block and index (#19712)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-14 15:23:46 +00:00
Alexey Shekhirin
b6e6bd35c0 refactor(stages): empty transactions range (#19753) 2025-11-14 15:11:34 +00:00
Avory
d150b0a39b docs: remove obsolete max_changesets documentation (#19731) 2025-11-14 14:57:01 +00:00
Arsenii Kulikov
63409fe650 chore: bump revm (#19757) 2025-11-14 14:33:40 +00:00
cui
7191e9ca10 feat: add --netrestrict to node cmd like in geth (#19686)
Co-authored-by: weixie.cui <weixie.cui@okg.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-14 13:08:51 +00:00
Gengar
a21a3cc0d6 docs: add comprehensive documentation for NodeType enum (#19740) 2025-11-14 13:00:37 +00:00
radik878
26f575440d docs(stages): align stages.md with current pipeline and PoS semantics (#19733) 2025-11-14 12:58:27 +00:00
YK
3ac5bf4dd1 feat(bench-compare): add configurable OTLP trace queue size (#19737)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-14 12:55:33 +00:00
Chad
cce85b310b feat: make PayloadBuilderArgs defaults customizable (#19696)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-11-14 12:52:28 +00:00
Fallengirl
60d5687dbd docs: resolve broken intra-doc link in transactions module (#19744) 2025-11-14 12:43:45 +00:00
Matthias Seitz
20270ec616 fix: add minbasefee for jovian attributes (#19726) 2025-11-14 12:02:20 +00:00
Matthias Seitz
fb8b28fced feat: add --skip-invalid-blocks (#19750) 2025-11-14 11:27:09 +00:00
Matthias Seitz
bc7803dbe0 chore: use hex! macro in chainspec tests (#19671) 2025-11-14 09:57:38 +00:00
Matthias Seitz
947b67e8c6 chore: reset log interval on new stats (#19693) 2025-11-14 09:55:33 +00:00
Brian Picciano
531c8f4ced feat(db): Add metrics/spans to OverlayStateProviderFactory (#19745) 2025-11-14 09:47:41 +00:00
Matthias Seitz
cfd0e3f5ed chore: improve test database error messages (#19557) 2025-11-14 09:40:44 +00:00
YK
6db85df46d fix(bench-compare): fix CSV parser schema (#19742) 2025-11-14 08:47:08 +00:00
Matthias Seitz
326165185d feat: add more block meta to comparison (#19722)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-14 00:47:28 +00:00
Dan Cline
2233f8661c feat(docs): Add section about setting up Jaeger and OTLP (#19628)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-13 23:37:52 +00:00
Mablr
bedbfb83f3 refactor(rpc-convert): move rpc conversion traits/impls to alloy-evm (#19616) 2025-11-13 23:01:43 +00:00
forkfury
ba84eeaccd docs(revm): document lifetime requirements for CachedReads wrappers (#19725) 2025-11-13 22:58:02 +00:00
youyyytrok
bff7ddcdf3 docs: fixed dead Sentry link (#19714) 2025-11-13 22:57:33 +00:00
oooLowNeoNooo
c51cf92db0 fix(stateless): export stateless_validation function (#19729) 2025-11-13 20:56:06 +00:00
Matthias Seitz
86246b6f4b fix: ensure open db tx is dropped (#19727) 2025-11-13 20:07:19 +00:00
Léa Narzis
96993dd073 refactor(era-downloader): support both era and era1 file types in downloader (#19617) 2025-11-13 19:26:21 +00:00
Ignacio Hagopian
c5b7d4a58a chore(stateless): show wrapped error message (#19716)
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
2025-11-13 18:05:04 +00:00
Alexey Shekhirin
ce2dc9203b feat(cli): reth db static-file-header (#19719) 2025-11-13 17:48:53 +00:00
Brian Picciano
d77e4815c3 feat(bench): Disable log styling in reth-bench-compare sub-processes (#19718) 2025-11-13 16:52:59 +00:00
joshieDo
88d853e724 feat: always write receipts to static files on new nodes (#19399) 2025-11-13 16:37:30 +00:00
Arsenii Kulikov
ca33e8a457 chore: bump revm-interpreter (#19709) 2025-11-13 12:19:04 +00:00
David Klank
270fb977ba fix(db-api): resolve test compilation errors (#19704)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-13 09:39:06 +00:00
Fibonacci747
a96b22875b chore: remove redundant eth/68 NewPooledTransactionHashes length validation (#19708) 2025-11-13 09:29:21 +00:00
0xLogicalx
7a599dc130 chore: remove redundant clone in exex subscription RPC setup (#19699) 2025-11-13 00:09:19 +00:00
Alex Pikme
474351a929 feat(op-rpc): support batch rpc calls in historical forwarding (#19679)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-12 23:32:35 +00:00
Dan Cline
95d8916825 chore(engine): add basic tx result information to execution spans (#19698) 2025-11-12 23:08:11 +00:00
Dan Cline
75e9359fec chore(cli): disable long read transaction for db list and pipeline (#19694) 2025-11-12 21:19:02 +00:00
Merkel Tranjes
ec92a839fc refactor(stages): use named structs for ExecInput returns (#19689) 2025-11-12 17:54:07 +00:00
Brian Picciano
573191e1d1 chore(trie): Allow reusing Hashed/TrieCursors (#19588) 2025-11-12 17:31:04 +00:00
Alexey Shekhirin
95b8a8535b feat(stages): get transaction range starting from first available block (#19662) 2025-11-12 14:43:39 +00:00
Alexey Shekhirin
c57a5204c2 refactor(provider): explicit static file segment matches (#19664)
Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com>
2025-11-12 14:31:48 +00:00
leniram159
6c1296da5d feat: support block number in required-block-hashes parameter (#18546)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-12 13:52:38 +00:00
Brian Picciano
3f1a7b37e9 chore(trie): Add metrics to track all cursor operations in proof workers (#19633) 2025-11-12 13:35:37 +00:00
Andrés David Ramírez Chiquillo
55a49080c6 feat(db): introduce --db.page-size argument (#19594) 2025-11-12 13:21:56 +00:00
phrwlk
40f89af926 chore: remove unused latest_update_kind from TxPool (#19634) 2025-11-12 13:20:36 +00:00
josé v
a7a4c3bf59 chore: make extra_data_size_limit configurable in EthBeaconConsensus (#19496) 2025-11-12 13:15:47 +00:00
Avory
abe6bf6125 fix(stages): implement floor rounding for percentage in no_std mode (#19675) 2025-11-12 12:57:32 +00:00
Merkel Tranjes
54cca9efd0 fix(hardforks): don't print pre-merge section if empty (#19654)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-12 12:52:43 +00:00
Francis Li
12fd25892d refactor(flashblock): Move all flashblocks related data structure to op-alloy (#19608) 2025-11-12 12:26:42 +00:00
strmfos
43407d47f9 fix: require config path when not using --default (#19632) 2025-11-12 12:16:34 +00:00
Fibonacci747
385fcddbe0 fix(ethstats): prevent shutdown on read errors (#19672) 2025-11-12 12:15:08 +00:00
cui
8479f286ea feat: add --p2p-secret-key-hex which is similiar nodekeyhex in geth (#19670)
Co-authored-by: weixie.cui <weixie.cui@okg.com>
2025-11-12 12:15:03 +00:00
Delweng
d9537a416a feat(rpc): debug_traceCall support TxIndex (#18477)
Signed-off-by: jsvisa <delweng@gmail.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-12 11:49:04 +00:00
Arsenii Kulikov
bacb3522bb chore: bump revm v33 (#19674) 2025-11-12 11:46:40 +00:00
Salman Pathan
7b89167ce1 chore: add target: flashblock for all flashblock related traces (#19656) 2025-11-12 11:07:21 +00:00
Karl Yu
56ded417e9 feat: limit handling of incoming txs to trusted peers (#19666)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-12 10:45:26 +00:00
bigbear
2e5f6f8323 fix: preserve parent beacon root in pending env (#19645) 2025-11-12 09:29:43 +00:00
Matthias Seitz
b3c3affc5f chore: downgrade noisy log (#19660) 2025-11-12 09:28:26 +00:00
Alexey Shekhirin
64f33cf648 feat(node): allow to configure blocks per file for static file segments (#19562) 2025-11-12 09:23:08 +00:00
Hai | RISE
967edb541e fix: fix new casting error in signal handler (#19669) 2025-11-12 10:34:21 +01:00
Hai | RISE
ef2d9da53b fix(tree-engine): also update in-memory tree when there are attributes in an unwind forkchoice (#19668) 2025-11-12 10:34:00 +01:00
Alexey Shekhirin
01f497bc49 test(static-file): segment header serialization roundtrip and snapshots (#19657) 2025-11-11 20:02:09 +00:00
Alexey Shekhirin
151ae651ad ci: use macos-14 runner (#19658) 2025-11-11 18:37:19 +01:00
Alexey Shekhirin
c780256158 feat(cli): log warning if otlp feature is not enabled (#19648) 2025-11-11 16:38:47 +00:00
Ragnar
3c39444597 fix(stages): correct tip_tx field comment in PipelineBuilder (#19655) 2025-11-11 16:00:28 +00:00
YK
3a672ee0a4 feat(bench-compare): add OTLP tracing support for baseline/feature comparison (#19626) 2025-11-11 15:49:48 +00:00
YK
08dc9cb096 fix(metrics): move trie_input_duration recording to capture full setup (#19649) 2025-11-11 15:47:48 +00:00
Abhivansh
9bc2bf23f4 refactor: capped to to best_block (#19640) 2025-11-11 12:59:30 +00:00
Alexey Shekhirin
087b1665f1 chore: bump version to 1.9.2 (#19647) 2025-11-11 13:22:42 +01:00
Alexey Shekhirin
93003560cf revert: "refactor(prune): remove receipts log filter segment (#19184)" (#19646) 2025-11-11 12:01:39 +00:00
stevencartavia
5f6229fc56 feat: add support for eip-7872 Max blob flag for local builders (#19614) 2025-11-11 10:33:35 +00:00
Alexey Shekhirin
1c256b00ff refactor(provider): introduce EitherWriter::new_receipts (#19600) 2025-11-11 10:29:20 +00:00
YK
4621456ef9 feat(bench-compare): add block range to comparison report (#19643) 2025-11-11 09:31:37 +00:00
Dan Cline
cbc80bab7f chore(static-file): add note on underscores in names (#19639) 2025-11-10 22:18:43 +00:00
rakita
7b3300841f chore: bump op-revm v12.0.2 patch (#19629) 2025-11-10 14:53:28 +00:00
pepes
21b9e3af50 fix: log correct account worker count (#19619) 2025-11-10 12:16:04 +00:00
Matthias Seitz
2e5ac1ce13 chore: add feature propagation to sdk (#19627) 2025-11-10 11:52:30 +00:00
Léa Narzis
b539348bb5 feat(cli): add cli flag --txpool.disable-blobs-support to disable blob support (#19559)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-09 19:32:14 +00:00
Gengar
db1737ea87 docs(chainspec): improve fork_id and satisfy documentation (#19523) 2025-11-09 19:24:46 +00:00
github-actions[bot]
9a0418032d chore(deps): weekly cargo update (#19607)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-09 11:57:19 +00:00
Adrian
a07b83f2fd fix(chainspec): use correct Jovian timestamp constant for Base Sepolia test (#19606) 2025-11-09 11:57:06 +00:00
YK
4b8d2e8774 fix(codspeed): add harness = false for update benchmark in reth-trie-sparse (#19599) 2025-11-08 14:08:48 +00:00
Dan Cline
43e5cc7989 chore(trie): add number of target slots to storage proof span (#19590) 2025-11-08 08:37:32 +00:00
sashass1315
414e995c3d fix(storage): propagate IO errors in parse_accounts to avoid silent partial imports (#19582) 2025-11-08 06:34:20 +00:00
stevencartavia
ec7e8d450b chore: remove unused event variant in ConsensusEngineEvent enum (#19596) 2025-11-08 06:02:47 +00:00
Micke
32a80e8c49 perf(trie): replace static Vec with const slice for empty updates (#19499) 2025-11-07 17:55:00 +00:00
joshieDo
177ad4c0b8 chore: remove duplicated provider method transaction_block (#19585) 2025-11-07 15:03:39 +00:00
Brian Picciano
7faddbaaee chore(trie): Use Vec<Option<...>> in HashedPostStateCursors (#19487)
Co-authored-by: Francis Li <francis.li@uniswap.org>
Co-authored-by: YK <chiayongkang@hotmail.com>
2025-11-07 14:05:22 +00:00
Alexey Shekhirin
5006d5fa87 chore: bump version to 1.9.1 (#19578)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-07 12:08:22 +00:00
Alexey Shekhirin
b0310d1394 feat(engine): metric for time diff between newPayload end and next forkchoiceUpdated start (#19577) 2025-11-07 11:53:44 +00:00
Alexey Shekhirin
05cc14f547 chore: use instance label in Grafana dashboard (#19573) 2025-11-07 11:47:17 +00:00
Brian Picciano
b48c72fad2 chore: BuiltPayloadExecutedBlock (#19430) 2025-11-07 09:16:55 +00:00
Matthias Seitz
4394860df4 ci: add independent tagging options (#19574) 2025-11-07 09:48:43 +01:00
YK
5464312672 fix(bench-compare): validate remote git references (#19569) 2025-11-07 08:10:16 +00:00
Matthias Seitz
9f9ab671c3 ci: tag (#19572) 2025-11-07 09:17:44 +01:00
rakita
42ac2aaeb6 chore: bump revm v31.0.1 (#19567) 2025-11-07 08:34:56 +01:00
Vitalyr
4d258cb98d fix: update docs and arg comments (#19537)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-07 00:31:09 +00:00
Matthias Seitz
f69c544da6 refactor: replace GenericArray with regular arrays in ECIES (#19563) 2025-11-06 23:43:58 +00:00
Matthias Seitz
e813681c5d feat(debug): re-establish block subscription (#19550) 2025-11-06 23:05:11 +00:00
Alexey Shekhirin
3385ec5e6e test(static-file): StaticFileSegment string and serde roundtrips (#19561) 2025-11-06 22:34:13 +00:00
MIHAO PARK
7ade95e9fa chore(consensus): update GasLimitInvalidIncrease/GasLimitInvalidDecrease error msg (#18561)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-06 21:53:45 +00:00
Arsenii Kulikov
4d9d712b43 refactor: provide default implementation for send_raw_transaction (#19564) 2025-11-06 21:33:57 +00:00
phrwlk
cb78b9da67 fix(net): preserve ECIESError in connect_without_timeout (#19558)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-06 21:26:07 +00:00
Alexey Shekhirin
7997cd4283 chore(provider): exhaustive segment match in static files check (#19556) 2025-11-06 18:06:43 +00:00
Alexey Shekhirin
27cf27a984 refactor(provider): introduce EitherWriter (#19554) 2025-11-06 17:36:03 +00:00
Erce Can Bektüre
a6c0ac8cf4 fix: Update filter last poll timestamp in filter logs (#19549) 2025-11-06 16:20:50 +00:00
Snezhkko
04093cc3be fix(prune): use saturating_sub in PruneLimiter::deleted_entries_limit_left (#19535) 2025-11-06 15:49:08 +00:00
Alexey Shekhirin
c5870312e4 feat(static-file): dynamic static file size (#19381) 2025-11-06 14:34:05 +00:00
Andrés Ramírez-Chiquillo
d81d547c93 docs: explain default db size and error in CLI help (#19533)
Co-authored-by: ¨Andrurachi¨ <¨andruvrch@gmail.com¨>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-06 13:33:15 +00:00
phrwlk
65acaf330e fix: remove redundant header insertion in extend_blocks and tests (#19534) 2025-11-06 12:51:18 +00:00
Karl Yu
9d9c1d2824 feat: add capabilities to fetch Peer type (#19543) 2025-11-06 12:16:32 +00:00
Alexey Shekhirin
4f1f2d8033 ci: check that Grafana dashboard doesn't contain DS_PROMETHEUS (#19541) 2025-11-06 11:39:04 +00:00
Alexey Shekhirin
671c690cc1 chore: add new engine metrics to dashboard, fix multiproof charts (#19540) 2025-11-06 11:39:01 +00:00
Alexey Shekhirin
b947455061 refactor(provider, cli): simplify getting provider for index or range (#19440) 2025-11-06 11:34:51 +00:00
Alexey Shekhirin
e5c47fe350 feat(provider): configurable blocks per static file segment (#19458) 2025-11-06 10:53:38 +00:00
David Klank
7cc4fdfaeb fix(stages-types): resolve compilation errors in tests (#19501) 2025-11-06 10:35:22 +00:00
Brian Picciano
ea4a686e86 fix(trie): InMemoryTrieCursor case where all DB nodes are deleted (#19464) 2025-11-06 09:19:39 +00:00
Avory
791de250d7 perf(era-utils): avoid unnecessary PathBuf clone in export (#19530) 2025-11-06 09:04:24 +00:00
oooLowNeoNooo
0928059f5c fix: replace unreachable libmdbx documentation URL (#19532) 2025-11-06 08:24:25 +00:00
Alexey Shekhirin
99fe175823 feat(engine): record newPayload/forkchoiceUpdated metrics outside of RPC (#19522) 2025-11-06 07:47:03 +00:00
joshieDo
e20e56b75e feat: add Metadata table and StorageSettings to ProviderFactory (#19384) 2025-11-06 00:39:49 +00:00
Léa Narzis
e3b38b2de5 chore(era): move era types to era module (#19527) 2025-11-05 19:42:07 +00:00
Fibonacci747
ba8be3fb64 feat(optimism): Simplify trait bounds in revalidate_interop_txs_stream (#19500) 2025-11-05 17:16:57 +00:00
Maxim Evtush
2bcd7388d7 docs: fix license links in README.md (#19519) 2025-11-05 17:16:04 +00:00
Léa Narzis
2ba17cf10d refactor(era): move era types and file handling to new module (#19520) 2025-11-05 17:07:45 +00:00
David Klank
629363a6ea refactor: use Url::as_str() directly in era modules (#19485) 2025-11-05 17:07:10 +00:00
Alexey Shekhirin
11d28b1abb chore: use dashboard variable in main Grafana dashboard (#19518) 2025-11-05 15:48:01 +00:00
Léa Narzis
1b5f1293bc refactor(era): move to e2s module e2s types and file handling (#19490) 2025-11-05 15:40:32 +00:00
776 changed files with 50244 additions and 11579 deletions

View File

@@ -15,3 +15,12 @@ slow-timeout = { period = "2m", terminate-after = 10 }
[[profile.default.overrides]]
filter = "binary(e2e_testsuite)"
slow-timeout = { period = "2m", terminate-after = 3 }
[[profile.default.overrides]]
filter = "package(reth-era) and binary(it)"
slow-timeout = { period = "2m", terminate-after = 10 }
# Allow slower ethereum node e2e tests (p2p + blobs) to run up to 5 minutes.
[[profile.default.overrides]]
filter = "package(reth-node-ethereum) and binary(e2e)"
slow-timeout = { period = "1m", terminate-after = 5 }

View File

@@ -12,7 +12,7 @@ workflows:
# Check that `A` activates the features of `B`.
"propagate-feature",
# These are the features to check:
"--features=std,op,dev,asm-keccak,jemalloc,jemalloc-prof,tracy-allocator,serde-bincode-compat,serde,test-utils,arbitrary,bench,alloy-compat,min-error-logs,min-warn-logs,min-info-logs,min-debug-logs,min-trace-logs,otlp,js-tracer",
"--features=std,op,dev,asm-keccak,jemalloc,jemalloc-prof,tracy-allocator,serde-bincode-compat,serde,test-utils,arbitrary,bench,alloy-compat,min-error-logs,min-warn-logs,min-info-logs,min-debug-logs,min-trace-logs,otlp,js-tracer,portable,keccak-cache-global",
# Do not try to add a new section to `[features]` of `A` only because `B` exposes that feature. There are edge-cases where this is still needed, but we can add them manually.
"--left-side-feature-missing=ignore",
# Ignore the case that `A` it outside of the workspace. Otherwise it will report errors in external dependencies that we have no influence on.

View File

@@ -4,6 +4,7 @@
# include source files
!/bin
!/crates
!/pkg
!/testing
!book.toml
!Cargo.lock
@@ -11,6 +12,7 @@
!Cross.toml
!deny.toml
!Makefile
!README.md
# include for vergen constants
!/.git

1
.github/CODEOWNERS vendored
View File

@@ -40,5 +40,6 @@ crates/tasks/ @mattsse
crates/tokio-util/ @fgimenez
crates/transaction-pool/ @mattsse @yongkangc
crates/trie/ @Rjected @shekhirin @mediocregopher
bin/reth-bench-compare/ @mediocregopher @shekhirin @yongkangc
etc/ @Rjected @shekhirin
.github/ @gakonst @DaniPopes

7
.github/actionlint.yaml vendored Normal file
View File

@@ -0,0 +1,7 @@
self-hosted-runner:
labels:
- depot-ubuntu-latest
- depot-ubuntu-latest-2
- depot-ubuntu-latest-4
- depot-ubuntu-latest-8
- depot-ubuntu-latest-16

View File

@@ -7,7 +7,7 @@ sim="${1}"
limit="${2}"
run_hive() {
hive --sim "${sim}" --sim.limit "${limit}" --sim.parallelism 8 --client reth 2>&1 | tee /tmp/log || true
hive --sim "${sim}" --sim.limit "${limit}" --sim.parallelism 16 --client reth 2>&1 | tee /tmp/log || true
}
check_log() {

View File

@@ -11,18 +11,19 @@ env:
CARGO_TERM_COLOR: always
BASELINE: base
SEED: reth
RUSTC_WRAPPER: "sccache"
name: bench
jobs:
codspeed:
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
submodules: true
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

View File

@@ -10,13 +10,16 @@ on:
types: [opened, reopened, synchronize, closed]
merge_group:
env:
RUSTC_WRAPPER: "sccache"
jobs:
build:
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest-8
timeout-minutes: 90
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install bun
uses: oven-sh/setup-bun@v2
@@ -33,6 +36,8 @@ jobs:
- name: Install Rust nightly
uses: dtolnay/rust-toolchain@nightly
- uses: mozilla-actions/sccache-action@v0.0.9
- name: Build docs
run: cd docs/vocs && bash scripts/build-cargo-docs.sh

View File

@@ -13,12 +13,12 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
name: compact-codec
jobs:
compact-codec:
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
strategy:
matrix:
bin:
@@ -27,11 +27,12 @@ jobs:
steps:
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: Checkout base
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
ref: ${{ github.base_ref || 'main' }}
# On `main` branch, generates test vectors and serializes them to disk using `Compact`.
@@ -39,7 +40,7 @@ jobs:
run: |
${{ matrix.bin }} -- test-vectors compact --write
- name: Checkout PR
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
clean: false
# On incoming merge try to read and decode previously generated vectors with `Compact`

View File

@@ -33,7 +33,7 @@ jobs:
- name: 'Build and push the git-sha-tagged op-reth image'
command: 'make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME GIT_SHA=$GIT_SHA PROFILE=maxperf op-docker-build-push-git-sha'
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2

View File

@@ -35,7 +35,7 @@ jobs:
- name: 'Build and push the nightly profiling op-reth image'
command: 'make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=profiling op-docker-build-push-nightly-profiling'
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Remove bloatware
uses: laverdet/remove-bloatware@v1.0.0
with:

73
.github/workflows/docker-tag-latest.yml vendored Normal file
View File

@@ -0,0 +1,73 @@
# Tag a specific Docker release version as latest
name: docker-tag-latest
on:
workflow_dispatch:
inputs:
version:
description: 'Release version to tag as latest (e.g., v1.8.4)'
required: true
type: string
tag_reth:
description: 'Tag reth image as latest'
required: false
type: boolean
default: true
tag_op_reth:
description: 'Tag op-reth image as latest'
required: false
type: boolean
default: false
env:
DOCKER_USERNAME: ${{ github.actor }}
jobs:
tag-reth-latest:
name: Tag reth as latest
runs-on: ubuntu-24.04
if: ${{ inputs.tag_reth }}
permissions:
packages: write
contents: read
steps:
- name: Log in to Docker
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io --username ${DOCKER_USERNAME} --password-stdin
- name: Pull reth release image
run: |
docker pull ghcr.io/${{ github.repository_owner }}/reth:${{ inputs.version }}
- name: Tag reth as latest
run: |
docker tag ghcr.io/${{ github.repository_owner }}/reth:${{ inputs.version }} ghcr.io/${{ github.repository_owner }}/reth:latest
- name: Push reth latest tag
run: |
docker push ghcr.io/${{ github.repository_owner }}/reth:latest
tag-op-reth-latest:
name: Tag op-reth as latest
runs-on: ubuntu-24.04
if: ${{ inputs.tag_op_reth }}
permissions:
packages: write
contents: read
steps:
- name: Log in to Docker
run: |
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io --username ${DOCKER_USERNAME} --password-stdin
- name: Pull op-reth release image
run: |
docker pull ghcr.io/${{ github.repository_owner }}/op-reth:${{ inputs.version }}
- name: Tag op-reth as latest
run: |
docker tag ghcr.io/${{ github.repository_owner }}/op-reth:${{ inputs.version }} ghcr.io/${{ github.repository_owner }}/op-reth:latest
- name: Push op-reth latest tag
run: |
docker push ghcr.io/${{ github.repository_owner }}/op-reth:latest

View File

@@ -32,7 +32,7 @@ jobs:
- name: "Build and push op-reth image"
command: "make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
@@ -68,7 +68,7 @@ jobs:
- name: "Build and push op-reth image"
command: "make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push-latest"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2

View File

@@ -11,6 +11,7 @@ on:
env:
CARGO_TERM_COLOR: always
SEED: rustethereumethereumrust
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -19,14 +20,14 @@ concurrency:
jobs:
test:
name: e2e-testsuite
runs-on:
group: Reth
runs-on: depot-ubuntu-latest-4
env:
RUST_BACKTRACE: 1
timeout-minutes: 90
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: taiki-e/install-action@nextest
- uses: Swatinem/rust-cache@v2
with:
@@ -43,4 +44,3 @@ jobs:
--exclude 'op-reth' \
--exclude 'reth' \
-E 'binary(e2e_testsuite)'

21
.github/workflows/grafana.yml vendored Normal file
View File

@@ -0,0 +1,21 @@
name: grafana
on:
pull_request:
merge_group:
push:
branches: [main]
jobs:
check-dashboard:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Check for ${DS_PROMETHEUS} in overview.json
run: |
if grep -Fn '${DS_PROMETHEUS}' etc/grafana/dashboards/overview.json; then
echo "Error: overview.json contains '\${DS_PROMETHEUS}' placeholder"
echo "Please replace it with '\${datasource}'"
exit 1
fi
echo "✓ overview.json does not contain '\${DS_PROMETHEUS}' placeholder"

View File

@@ -24,12 +24,11 @@ jobs:
prepare-hive:
if: github.repository == 'paradigmxyz/reth'
timeout-minutes: 45
runs-on:
group: Reth
runs-on: depot-ubuntu-latest-16
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Checkout hive tests
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: ethereum/hive
path: hivetests
@@ -45,7 +44,7 @@ jobs:
- name: Restore hive assets cache
id: cache-hive
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ./hive_assets
key: hive-assets-${{ steps.hive-commit.outputs.hash }}-${{ hashFiles('.github/assets/hive/build_simulators.sh') }}
@@ -68,7 +67,7 @@ jobs:
chmod +x hive
- name: Upload hive assets
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: hive_assets
path: ./hive_assets
@@ -179,23 +178,22 @@ jobs:
- prepare-reth
- prepare-hive
name: run ${{ matrix.scenario.sim }}${{ matrix.scenario.limit && format(' - {0}', matrix.scenario.limit) }}
runs-on:
group: Reth
runs-on: depot-ubuntu-latest-16
permissions:
issues: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Download hive assets
uses: actions/download-artifact@v6
uses: actions/download-artifact@v7
with:
name: hive_assets
path: /tmp
- name: Download reth image
uses: actions/download-artifact@v6
uses: actions/download-artifact@v7
with:
name: artifacts
path: /tmp
@@ -209,7 +207,7 @@ jobs:
chmod +x /usr/local/bin/hive
- name: Checkout hive tests
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: ethereum/hive
ref: master
@@ -247,8 +245,7 @@ jobs:
notify-on-error:
needs: test
if: failure()
runs-on:
group: Reth
runs-on: ubuntu-latest
steps:
- name: Slack Webhook Action
uses: rtCamp/action-slack-notify@v2

View File

@@ -14,6 +14,7 @@ on:
env:
CARGO_TERM_COLOR: always
SEED: rustethereumethereumrust
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -23,8 +24,7 @@ jobs:
test:
name: test / ${{ matrix.network }}
if: github.event_name != 'schedule'
runs-on:
group: Reth
runs-on: depot-ubuntu-latest-4
env:
RUST_BACKTRACE: 1
strategy:
@@ -32,12 +32,13 @@ jobs:
network: ["ethereum", "optimism"]
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- name: Install Geth
run: .github/assets/install_geth.sh
- uses: taiki-e/install-action@nextest
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -71,12 +72,13 @@ jobs:
if: github.event_name == 'schedule'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@nextest
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: run era1 files integration tests
run: cargo nextest run --package reth-era --test it -- --ignored
run: cargo nextest run --release --package reth-era --test it -- --ignored

View File

@@ -9,7 +9,7 @@ on:
push:
tags:
- '*'
- "*"
env:
CARGO_TERM_COLOR: always
@@ -32,17 +32,16 @@ jobs:
strategy:
fail-fast: false
name: run kurtosis
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
needs:
- prepare-reth
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Download reth image
uses: actions/download-artifact@v6
uses: actions/download-artifact@v7
with:
name: artifacts
path: /tmp
@@ -83,12 +82,10 @@ jobs:
kurtosis service logs -a op-devnet op-cl-2151908-2-op-node-op-reth-op-kurtosis
exit 1
notify-on-error:
needs: test
if: failure()
runs-on:
group: Reth
runs-on: ubuntu-latest
steps:
- name: Slack Webhook Action
uses: rtCamp/action-slack-notify@v2

View File

@@ -9,7 +9,7 @@ on:
push:
tags:
- '*'
- "*"
env:
CARGO_TERM_COLOR: always
@@ -30,17 +30,16 @@ jobs:
strategy:
fail-fast: false
name: run kurtosis
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
needs:
- prepare-reth
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Download reth image
uses: actions/download-artifact@v6
uses: actions/download-artifact@v7
with:
name: artifacts
path: /tmp
@@ -54,13 +53,12 @@ jobs:
- name: Run kurtosis
uses: ethpandaops/kurtosis-assertoor-github-action@v1
with:
ethereum_package_args: '.github/assets/kurtosis_network_params.yaml'
ethereum_package_args: ".github/assets/kurtosis_network_params.yaml"
notify-on-error:
needs: test
if: failure()
runs-on:
group: Reth
runs-on: ubuntu-latest
steps:
- name: Slack Webhook Action
uses: rtCamp/action-slack-notify@v2

View File

@@ -11,7 +11,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0

View File

@@ -12,7 +12,7 @@ jobs:
actionlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Download actionlint
id: get_actionlint
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)

View File

@@ -8,11 +8,12 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
jobs:
clippy-binaries:
name: clippy binaries / ${{ matrix.type }}
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
@@ -21,11 +22,12 @@ jobs:
args: --workspace --lib --examples --tests --benches --locked
features: "ethereum asm-keccak jemalloc jemalloc-prof min-error-logs min-warn-logs min-info-logs min-debug-logs min-trace-logs"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@clippy
with:
components: clippy
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -40,14 +42,15 @@ jobs:
clippy:
name: clippy
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@nightly
with:
components: clippy
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -56,15 +59,16 @@ jobs:
RUSTFLAGS: -D warnings
wasm:
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
with:
target: wasm32-wasip1
- uses: taiki-e/install-action@cargo-hack
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -75,15 +79,16 @@ jobs:
.github/assets/check_wasm.sh
riscv:
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
with:
target: riscv32imac-unknown-none-elf
- uses: taiki-e/install-action@cargo-hack
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -92,21 +97,27 @@ jobs:
run: .github/assets/check_rv32imac.sh
crate-checks:
runs-on: ubuntu-latest
timeout-minutes: 30
name: crate-checks (${{ matrix.partition }}/${{ matrix.total_partitions }})
runs-on: depot-ubuntu-latest-4
strategy:
matrix:
partition: [1, 2, 3]
total_partitions: [3]
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@cargo-hack
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- run: cargo hack check --workspace
- run: cargo hack check --workspace --partition ${{ matrix.partition }}/${{ matrix.total_partitions }}
msrv:
name: MSRV
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
@@ -114,11 +125,12 @@ jobs:
- binary: reth
- binary: op-reth
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.88" # MSRV
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -128,12 +140,13 @@ jobs:
docs:
name: docs
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest-4
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@nightly
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -145,25 +158,27 @@ jobs:
fmt:
name: fmt
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
- uses: mozilla-actions/sccache-action@v0.0.9
- name: Run fmt
run: cargo fmt --all --check
udeps:
name: udeps
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@nightly
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -172,19 +187,21 @@ jobs:
book:
name: book
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@nightly
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- run: cargo build --bin reth --workspace --features ethereum
- run: cargo build --bin reth --workspace
- run: cargo build --bin op-reth --workspace
env:
RUSTFLAGS: -D warnings
- run: ./docs/cli/update.sh target/debug/reth
- run: ./docs/cli/update.sh target/debug/reth target/debug/op-reth
- name: Check docs changes
run: git diff --exit-code
@@ -192,7 +209,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: crate-ci/typos@v1
check-toml:
@@ -200,7 +217,7 @@ jobs:
timeout-minutes: 30
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Run dprint
uses: dprint/check@v2.3
with:
@@ -210,7 +227,7 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Check dashboard JSON with jq
uses: sergeysova/jq-action@v2
with:
@@ -220,37 +237,45 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- name: Ensure no arbitrary or proptest dependency on default build
run: cargo tree --package reth -e=features,no-dev | grep -Eq "arbitrary|proptest" && exit 1 || exit 0
# Checks that selected rates can compile with power set of features
# Checks that selected crates can compile with power set of features
features:
name: features
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@clippy
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
- name: cargo install cargo-hack
uses: taiki-e/install-action@cargo-hack
- run: make check-features
- run: |
cargo hack check \
--package reth-codecs \
--package reth-primitives-traits \
--package reth-primitives \
--feature-powerset \
--depth 2
env:
RUSTFLAGS: -D warnings
# Check crates correctly propagate features
feature-propagation:
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: rui314/setup-mold@v1
- uses: taiki-e/cache-cargo-install-action@v2
with:

View File

@@ -26,10 +26,9 @@ jobs:
prepare-reth:
if: github.repository == 'paradigmxyz/reth'
timeout-minutes: 45
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- run: mkdir artifacts
- name: Set up Docker Buildx
@@ -51,7 +50,7 @@ jobs:
- name: Upload reth image
id: upload
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: artifacts
path: ./artifacts

View File

@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Update Homebrew formula
uses: dawidd6/action-homebrew-bump-formula@v5
uses: dawidd6/action-homebrew-bump-formula@v7
with:
token: ${{ secrets.HOMEBREW }}
no_fork: true

View File

@@ -1,11 +1,11 @@
# This workflow is for building and pushing reproducible Docker images for releases.
# This workflow is for building and pushing reproducible artifacts for releases
name: release-reproducible
on:
push:
tags:
- v*
workflow_run:
workflows: [release]
types: [completed]
env:
DOCKER_REPRODUCIBLE_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/reth-reproducible
@@ -13,23 +13,41 @@ env:
jobs:
extract-version:
name: extract version
if: ${{ github.event.workflow_run.conclusion == 'success' }}
runs-on: ubuntu-latest
steps:
- name: Extract version
run: echo "VERSION=$(echo ${GITHUB_REF#refs/tags/})" >> $GITHUB_OUTPUT
- name: Extract version from triggering tag
id: extract_version
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Get the tag that points to the head SHA of the triggering workflow
TAG=$(gh api /repos/${{ github.repository }}/git/refs/tags \
--jq '.[] | select(.object.sha == "${{ github.event.workflow_run.head_sha }}") | .ref' \
| head -1 \
| sed 's|refs/tags/||')
if [ -z "$TAG" ]; then
echo "No tag found for SHA ${{ github.event.workflow_run.head_sha }}"
exit 1
fi
echo "VERSION=$TAG" >> $GITHUB_OUTPUT
outputs:
VERSION: ${{ steps.extract_version.outputs.VERSION }}
build-reproducible:
name: build and push reproducible image
name: build and push reproducible image and binaries
runs-on: ubuntu-latest
needs: extract-version
needs: [extract-version]
permissions:
packages: write
contents: read
contents: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
ref: ${{ needs.extract-version.outputs.VERSION }}
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
@@ -40,12 +58,37 @@ jobs:
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push reproducible image
- name: Extract Rust version
id: rust_version
run: |
RUST_TOOLCHAIN=$(rustc --version | cut -d' ' -f2)
echo "RUST_TOOLCHAIN=$RUST_TOOLCHAIN" >> $GITHUB_OUTPUT
- name: Build reproducible artifacts
uses: docker/build-push-action@v6
id: docker_build
with:
context: .
file: ./Dockerfile.reproducible
build-args: |
RUST_TOOLCHAIN=${{ steps.rust_version.outputs.RUST_TOOLCHAIN }}
VERSION=${{ needs.extract-version.outputs.VERSION }}
target: artifacts
outputs: type=local,dest=./docker-artifacts
cache-from: type=gha
cache-to: type=gha,mode=max
env:
DOCKER_BUILD_RECORD_UPLOAD: false
- name: Build and push final image
uses: docker/build-push-action@v6
with:
context: .
file: ./Dockerfile.reproducible
push: true
build-args: |
RUST_TOOLCHAIN=${{ steps.rust_version.outputs.RUST_TOOLCHAIN }}
VERSION=${{ needs.extract-version.outputs.VERSION }}
tags: |
${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:${{ needs.extract-version.outputs.VERSION }}
${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:latest
@@ -54,3 +97,30 @@ jobs:
provenance: false
env:
DOCKER_BUILD_RECORD_UPLOAD: false
- name: Prepare artifacts from Docker build
run: |
mkdir reproducible-artifacts
cp docker-artifacts/reth reproducible-artifacts/reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu
cp docker-artifacts/*.deb reproducible-artifacts/reth-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu-reproducible.deb
- name: Configure GPG and create artifacts
env:
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: |
export GPG_TTY=$(tty)
echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --batch --import
cd reproducible-artifacts
tar -czf reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu.tar.gz reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu --remove-files
echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu.tar.gz
echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab reth-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu-reproducible.deb
- name: Upload reproducible artifacts to release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release upload ${{ needs.extract-version.outputs.VERSION }} \
reproducible-artifacts/*

View File

@@ -22,6 +22,7 @@ env:
CARGO_TERM_COLOR: always
DOCKER_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/reth
DOCKER_OP_IMAGE_NAME_URL: https://ghcr.io/${{ github.repository_owner }}/op-reth
RUSTC_WRAPPER: "sccache"
jobs:
dry-run:
@@ -49,8 +50,9 @@ jobs:
needs: extract-version
if: ${{ github.event.inputs.dry_run != 'true' }}
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- name: Verify crate version matches tag
# Check that the Cargo version starts with the tag,
# so that Cargo version 1.4.8 can be matched against both v1.4.8 and v1.4.8-rc.1
@@ -99,11 +101,12 @@ jobs:
- command: op-build
binary: op-reth
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
with:
target: ${{ matrix.configs.target }}
- uses: mozilla-actions/sccache-action@v0.0.9
- name: Install cross main
id: cross_main
run: |
@@ -141,14 +144,14 @@ jobs:
- name: Upload artifact
if: ${{ github.event.inputs.dry_run != 'true' }}
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz
path: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz
- name: Upload signature
if: ${{ github.event.inputs.dry_run != 'true' }}
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz.asc
path: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz.asc
@@ -166,11 +169,11 @@ jobs:
steps:
# This is necessary for generating the changelog.
# It has to come before "Download Artifacts" or else it deletes the artifacts.
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v6
uses: actions/download-artifact@v7
- name: Generate full changelog
id: changelog
run: |

View File

@@ -8,31 +8,73 @@ on:
jobs:
build:
name: build reproducible binaries
runs-on: ubuntu-latest
runs-on: ${{ matrix.runner }}
strategy:
matrix:
include:
- runner: ubuntu-latest
machine: machine-1
- runner: ubuntu-22.04
machine: machine-2
steps:
- uses: actions/checkout@v5
- uses: rui314/setup-mold@v1
- uses: actions/checkout@v6
- uses: dtolnay/rust-toolchain@stable
with:
target: x86_64-unknown-linux-gnu
- name: Install cross main
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build reproducible binary with Docker
run: |
cargo install cross --git https://github.com/cross-rs/cross
- name: Install cargo-cache
RUST_TOOLCHAIN=$(rustc --version | cut -d' ' -f2)
docker build \
--build-arg "RUST_TOOLCHAIN=${RUST_TOOLCHAIN}" \
-f Dockerfile.reproducible -t reth:release \
--target artifacts \
--output type=local,dest=./target .
- name: Calculate SHA256
id: sha256
run: |
cargo install cargo-cache
- uses: Swatinem/rust-cache@v2
sha256sum target/reth > checksum.sha256
echo "Binaries SHA256 on ${{ matrix.machine }}: $(cat checksum.sha256)"
- name: Upload the hash
uses: actions/upload-artifact@v6
with:
cache-on-failure: true
- name: Build Reth
name: checksum-${{ matrix.machine }}
path: |
checksum.sha256
retention-days: 1
compare:
name: compare reproducible binaries
needs: build
runs-on: ubuntu-latest
steps:
- name: Download artifacts from machine-1
uses: actions/download-artifact@v7
with:
name: checksum-machine-1
path: machine-1/
- name: Download artifacts from machine-2
uses: actions/download-artifact@v7
with:
name: checksum-machine-2
path: machine-2/
- name: Compare SHA256 hashes
run: |
make build-reproducible
mv target/x86_64-unknown-linux-gnu/release/reth reth-build-1
- name: Clean cache
run: make clean && cargo cache -a
- name: Build Reth again
run: |
make build-reproducible
mv target/x86_64-unknown-linux-gnu/release/reth reth-build-2
- name: Compare binaries
run: cmp reth-build-1 reth-build-2
echo "=== SHA256 Comparison ==="
echo "Machine 1 hash:"
cat machine-1/checksum.sha256
echo "Machine 2 hash:"
cat machine-2/checksum.sha256
if cmp -s machine-1/checksum.sha256 machine-2/checksum.sha256; then
echo "✅ SUCCESS: Binaries are identical (reproducible build verified)"
else
echo "❌ FAILURE: Binaries differ (reproducible build failed)"
exit 1
fi

View File

@@ -12,6 +12,7 @@ env:
CARGO_TERM_COLOR: always
FROM_BLOCK: 0
TO_BLOCK: 50000
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -22,16 +23,16 @@ jobs:
name: stage-run-test
# Only run stage commands test in merge groups
if: github.event_name == 'merge_group'
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
env:
RUST_LOG: info,sync=error
RUST_BACKTRACE: 1
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

View File

@@ -9,6 +9,7 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -17,8 +18,7 @@ concurrency:
jobs:
sync:
name: sync (${{ matrix.chain.bin }})
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
env:
RUST_LOG: info,sync=error
RUST_BACKTRACE: 1
@@ -39,9 +39,10 @@ jobs:
block: 10000
unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -64,4 +65,4 @@ jobs:
${{ matrix.chain.bin }} stage unwind num-blocks 100 --chain ${{ matrix.chain.chain }}
- name: Run stage unwind to block hash
run: |
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}

View File

@@ -9,6 +9,7 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -17,8 +18,7 @@ concurrency:
jobs:
sync:
name: sync (${{ matrix.chain.bin }})
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
env:
RUST_LOG: info,sync=error
RUST_BACKTRACE: 1
@@ -39,9 +39,10 @@ jobs:
block: 10000
unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de"
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -63,4 +64,4 @@ jobs:
${{ matrix.chain.bin }} stage unwind num-blocks 100 --chain ${{ matrix.chain.chain }}
- name: Run stage unwind to block hash
run: |
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}

View File

@@ -11,6 +11,7 @@ on:
env:
CARGO_TERM_COLOR: always
SEED: rustethereumethereumrust
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -19,8 +20,7 @@ concurrency:
jobs:
test:
name: test / ${{ matrix.type }} (${{ matrix.partition }}/${{ matrix.total_partitions }})
runs-on:
group: Reth
runs-on: depot-ubuntu-latest-4
env:
RUST_BACKTRACE: 1
strategy:
@@ -44,9 +44,10 @@ jobs:
total_partitions: 2
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -65,16 +66,15 @@ jobs:
state:
name: Ethereum state tests
runs-on:
group: Reth
runs-on: depot-ubuntu-latest-4
env:
RUST_LOG: info,sync=error
RUST_BACKTRACE: 1
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Checkout ethereum/tests
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
repository: ethereum/tests
ref: 81862e4848585a438d64f911a19b3825f0f4cd95
@@ -93,6 +93,7 @@ jobs:
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@nextest
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -100,15 +101,15 @@ jobs:
doc:
name: doc tests
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
env:
RUST_BACKTRACE: 1
timeout-minutes: 30
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install required tools
run: |
@@ -27,7 +27,7 @@ jobs:
./fetch_superchain_config.sh
- name: Create Pull Request
uses: peter-evans/create-pull-request@v7
uses: peter-evans/create-pull-request@v8
with:
commit-message: "chore: update superchain config"
title: "chore: update superchain config"

View File

@@ -9,18 +9,22 @@ on:
branches: [main]
merge_group:
env:
RUSTC_WRAPPER: "sccache"
jobs:
check-reth:
runs-on: ubuntu-24.04
runs-on: depot-ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
with:
target: x86_64-pc-windows-gnu
- uses: taiki-e/install-action@cross
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -30,16 +34,17 @@ jobs:
run: cargo check --target x86_64-pc-windows-gnu
check-op-reth:
runs-on: ubuntu-24.04
runs-on: depot-ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
with:
target: x86_64-pc-windows-gnu
- uses: taiki-e/install-action@cross
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

1249
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
[workspace.package]
version = "1.9.2"
version = "1.9.3"
edition = "2024"
rust-version = "1.88"
license = "MIT OR Apache-2.0"
@@ -153,6 +153,7 @@ members = [
"examples/custom-node-components/",
"examples/custom-payload-builder/",
"examples/custom-rlpx-subprotocol",
"examples/custom-rpc-middleware",
"examples/custom-node",
"examples/db-access",
"examples/engine-api-access",
@@ -328,6 +329,12 @@ inherits = "release"
lto = "fat"
codegen-units = 1
[profile.reproducible]
inherits = "release"
panic = "abort"
codegen-units = 1
incremental = false
[workspace.dependencies]
# reth
op-reth = { path = "crates/optimism/bin" }
@@ -369,11 +376,11 @@ reth-era-utils = { path = "crates/era-utils" }
reth-errors = { path = "crates/errors" }
reth-eth-wire = { path = "crates/net/eth-wire" }
reth-eth-wire-types = { path = "crates/net/eth-wire-types" }
reth-ethereum-payload-builder = { path = "crates/ethereum/payload" }
reth-ethereum-cli = { path = "crates/ethereum/cli", default-features = false }
reth-ethereum-consensus = { path = "crates/ethereum/consensus", default-features = false }
reth-ethereum-engine-primitives = { path = "crates/ethereum/engine-primitives", default-features = false }
reth-ethereum-forks = { path = "crates/ethereum/hardforks", default-features = false }
reth-ethereum-payload-builder = { path = "crates/ethereum/payload" }
reth-ethereum-primitives = { path = "crates/ethereum/primitives", default-features = false }
reth-ethereum = { path = "crates/ethereum/reth" }
reth-etl = { path = "crates/etl" }
@@ -466,68 +473,66 @@ reth-ress-protocol = { path = "crates/ress/protocol" }
reth-ress-provider = { path = "crates/ress/provider" }
# revm
revm = { version = "31.0.2", default-features = false }
revm = { version = "33.1.0", default-features = false }
revm-bytecode = { version = "7.1.1", default-features = false }
revm-database = { version = "9.0.5", default-features = false }
revm-state = { version = "8.1.1", default-features = false }
revm-primitives = { version = "21.0.2", default-features = false }
revm-interpreter = { version = "29.0.1", default-features = false }
revm-inspector = { version = "12.0.2", default-features = false }
revm-context = { version = "11.0.2", default-features = false }
revm-context-interface = { version = "12.0.1", default-features = false }
revm-interpreter = { version = "31.1.0", default-features = false }
revm-database-interface = { version = "8.0.5", default-features = false }
op-revm = { version = "12.0.2", default-features = false }
revm-inspectors = "0.32.0"
op-revm = { version = "14.1.0", default-features = false }
revm-inspectors = "0.33.2"
# eth
alloy-chains = { version = "0.2.5", default-features = false }
alloy-dyn-abi = "1.4.1"
alloy-eip2124 = { version = "0.2.0", default-features = false }
alloy-evm = { version = "0.23.0", default-features = false }
alloy-primitives = { version = "1.4.1", default-features = false, features = ["map-foldhash"] }
alloy-eip7928 = { version = "0.1.0" }
alloy-evm = { version = "0.25.1", default-features = false }
alloy-primitives = { version = "1.5.0", default-features = false, features = ["map-foldhash"] }
alloy-rlp = { version = "0.3.10", default-features = false, features = ["core-net"] }
alloy-sol-macro = "1.4.1"
alloy-sol-types = { version = "1.4.1", default-features = false }
alloy-sol-macro = "1.5.0"
alloy-sol-types = { version = "1.5.0", default-features = false }
alloy-trie = { version = "0.9.1", default-features = false }
alloy-hardforks = "0.4.4"
alloy-hardforks = "0.4.5"
alloy-consensus = { version = "1.0.41", default-features = false }
alloy-contract = { version = "1.0.41", default-features = false }
alloy-eips = { version = "1.0.41", default-features = false }
alloy-genesis = { version = "1.0.41", default-features = false }
alloy-json-rpc = { version = "1.0.41", default-features = false }
alloy-network = { version = "1.0.41", default-features = false }
alloy-network-primitives = { version = "1.0.41", default-features = false }
alloy-provider = { version = "1.0.41", features = ["reqwest"], default-features = false }
alloy-pubsub = { version = "1.0.41", default-features = false }
alloy-rpc-client = { version = "1.0.41", default-features = false }
alloy-rpc-types = { version = "1.0.41", features = ["eth"], default-features = false }
alloy-rpc-types-admin = { version = "1.0.41", default-features = false }
alloy-rpc-types-anvil = { version = "1.0.41", default-features = false }
alloy-rpc-types-beacon = { version = "1.0.41", default-features = false }
alloy-rpc-types-debug = { version = "1.0.41", default-features = false }
alloy-rpc-types-engine = { version = "1.0.41", default-features = false }
alloy-rpc-types-eth = { version = "1.0.41", default-features = false }
alloy-rpc-types-mev = { version = "1.0.41", default-features = false }
alloy-rpc-types-trace = { version = "1.0.41", default-features = false }
alloy-rpc-types-txpool = { version = "1.0.41", default-features = false }
alloy-serde = { version = "1.0.41", default-features = false }
alloy-signer = { version = "1.0.41", default-features = false }
alloy-signer-local = { version = "1.0.41", default-features = false }
alloy-transport = { version = "1.0.41" }
alloy-transport-http = { version = "1.0.41", features = ["reqwest-rustls-tls"], default-features = false }
alloy-transport-ipc = { version = "1.0.41", default-features = false }
alloy-transport-ws = { version = "1.0.41", default-features = false }
alloy-consensus = { version = "1.1.3", default-features = false }
alloy-contract = { version = "1.1.3", default-features = false }
alloy-eips = { version = "1.1.3", default-features = false }
alloy-genesis = { version = "1.1.3", default-features = false }
alloy-json-rpc = { version = "1.1.3", default-features = false }
alloy-network = { version = "1.1.3", default-features = false }
alloy-network-primitives = { version = "1.1.3", default-features = false }
alloy-provider = { version = "1.1.3", features = ["reqwest", "debug-api"], default-features = false }
alloy-pubsub = { version = "1.1.3", default-features = false }
alloy-rpc-client = { version = "1.1.3", default-features = false }
alloy-rpc-types = { version = "1.1.3", features = ["eth"], default-features = false }
alloy-rpc-types-admin = { version = "1.1.3", default-features = false }
alloy-rpc-types-anvil = { version = "1.1.3", default-features = false }
alloy-rpc-types-beacon = { version = "1.1.3", default-features = false }
alloy-rpc-types-debug = { version = "1.1.3", default-features = false }
alloy-rpc-types-engine = { version = "1.1.3", default-features = false }
alloy-rpc-types-eth = { version = "1.1.3", default-features = false }
alloy-rpc-types-mev = { version = "1.1.3", default-features = false }
alloy-rpc-types-trace = { version = "1.1.3", default-features = false }
alloy-rpc-types-txpool = { version = "1.1.3", default-features = false }
alloy-serde = { version = "1.1.3", default-features = false }
alloy-signer = { version = "1.1.3", default-features = false }
alloy-signer-local = { version = "1.1.3", default-features = false }
alloy-transport = { version = "1.1.3" }
alloy-transport-http = { version = "1.1.3", features = ["reqwest-rustls-tls"], default-features = false }
alloy-transport-ipc = { version = "1.1.3", default-features = false }
alloy-transport-ws = { version = "1.1.3", default-features = false }
# op
alloy-op-evm = { version = "0.23.0", default-features = false }
alloy-op-evm = { version = "0.25.0", default-features = false }
alloy-op-hardforks = "0.4.4"
op-alloy-rpc-types = { version = "0.22.0", default-features = false }
op-alloy-rpc-types-engine = { version = "0.22.0", default-features = false }
op-alloy-network = { version = "0.22.0", default-features = false }
op-alloy-consensus = { version = "0.22.0", default-features = false }
op-alloy-rpc-jsonrpsee = { version = "0.22.0", default-features = false }
op-alloy-rpc-types = { version = "0.23.1", default-features = false }
op-alloy-rpc-types-engine = { version = "0.23.1", default-features = false }
op-alloy-network = { version = "0.23.1", default-features = false }
op-alloy-consensus = { version = "0.23.1", default-features = false }
op-alloy-rpc-jsonrpsee = { version = "0.23.1", default-features = false }
op-alloy-flz = { version = "0.13.1", default-features = false }
# misc
@@ -549,8 +554,6 @@ dirs-next = "2.0.0"
dyn-clone = "1.0.17"
eyre = "0.6"
fdlimit = "0.3.0"
# pinned until downstream crypto libs migrate to 1.0 because 0.14.8 marks all types as deprecated
generic-array = "=0.14.7"
humantime = "2.1"
humantime-serde = "1.1"
itertools = { version = "0.14", default-features = false }
@@ -584,6 +587,7 @@ url = { version = "2.3", default-features = false }
zstd = "0.13"
byteorder = "1"
mini-moka = "0.10"
moka = "0.12"
tar-no-std = { version = "0.3.2", default-features = false }
miniz_oxide = { version = "0.8.4", default-features = false }
chrono = "0.4.41"
@@ -651,6 +655,9 @@ c-kzg = "2.1.5"
# config
toml = "0.8"
# rocksdb
rocksdb = { version = "0.24" }
# otlp obs
opentelemetry_sdk = "0.31"
opentelemetry = "0.31"
@@ -662,6 +669,7 @@ tracing-opentelemetry = "0.32"
arbitrary = "1.3"
assert_matches = "1.5.0"
criterion = { package = "codspeed-criterion-compat", version = "2.7" }
insta = "1.41"
proptest = "1.7"
proptest-derive = "0.5"
similar-asserts = { version = "1.5.0", features = ["serde"] }
@@ -730,6 +738,9 @@ visibility = "0.1.1"
walkdir = "2.3.3"
vergen-git2 = "1.0.5"
# networking
ipnet = "2.11"
# [patch.crates-io]
# alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
# alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }

View File

@@ -18,7 +18,7 @@ FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
# Build profile, release by default
ARG BUILD_PROFILE=release
ARG BUILD_PROFILE=maxperf
ENV BUILD_PROFILE=$BUILD_PROFILE
# Extra Cargo flags

View File

@@ -1,20 +1,25 @@
# Use the Rust 1.88 image based on Debian Bookworm
FROM rust:1.88-bookworm AS builder
ARG RUST_TOOLCHAIN=1.89.0
FROM docker.io/rust:$RUST_TOOLCHAIN-trixie AS builder
# Install specific version of libclang-dev
RUN apt-get update && apt-get install -y libclang-dev=1:14.0-55.7~deb12u1
# Copy the project to the container
COPY ./ /app
ARG PROFILE
ARG VERSION
# Switch to snapshot repository to pin dependencies
RUN sed -i '/^# http/{N;s|^# \(http[^ ]*\)\nURIs: .*|# \1\nURIs: \1|}' /etc/apt/sources.list.d/debian.sources
RUN apt-get -o Acquire::Check-Valid-Until=false update && \
apt-get install -y \
libjemalloc-dev \
libclang-dev \
mold
WORKDIR /app
COPY . .
RUN RUSTFLAGS_REPRODUCIBLE_EXTRA="-Clink-arg=-fuse-ld=mold" make build-reth-reproducible && \
PROFILE=${PROFILE:-reproducible} VERSION=$VERSION make build-deb-x86_64-unknown-linux-gnu
# Build the project with the reproducible settings
RUN make build-reproducible
FROM scratch AS artifacts
COPY --from=builder /app/target/x86_64-unknown-linux-gnu/reproducible/reth /reth
COPY --from=builder /app/target/x86_64-unknown-linux-gnu/reproducible/*.deb /
RUN mv /app/target/x86_64-unknown-linux-gnu/release/reth /reth
# Create a minimal final image with just the binary
FROM gcr.io/distroless/cc-debian12:nonroot-6755e21ccd99ddead6edc8106ba03888cbeed41a
COPY --from=builder /reth /reth
FROM gcr.io/distroless/cc-debian13:nonroot-239cdd2c8a6b275b6a6f6ed1428c57de2fff3e50
COPY --from=artifacts /reth /reth
EXPOSE 30303 30303/udp 9001 8545 8546
ENTRYPOINT [ "/reth" ]

View File

@@ -14,7 +14,7 @@ RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
ARG BUILD_PROFILE=release
ARG BUILD_PROFILE=maxperf
ENV BUILD_PROFILE=$BUILD_PROFILE
ARG RUSTFLAGS=""

View File

@@ -65,37 +65,31 @@ build: ## Build the reth binary into `target` directory.
cargo build --bin reth --features "$(FEATURES)" --profile "$(PROFILE)"
# Environment variables for reproducible builds
# Initialize RUSTFLAGS
RUST_BUILD_FLAGS =
# Enable static linking to ensure reproducibility across builds
RUST_BUILD_FLAGS += --C target-feature=+crt-static
# Set the linker to use static libgcc to ensure reproducibility across builds
RUST_BUILD_FLAGS += -C link-arg=-static-libgcc
# Remove build ID from the binary to ensure reproducibility across builds
RUST_BUILD_FLAGS += -C link-arg=-Wl,--build-id=none
# Remove metadata hash from symbol names to ensure reproducible builds
RUST_BUILD_FLAGS += -C metadata=''
# Set timestamp from last git commit for reproducible builds
SOURCE_DATE ?= $(shell git log -1 --pretty=%ct)
# Disable incremental compilation to avoid non-deterministic artifacts
CARGO_INCREMENTAL_VAL = 0
# Set C locale for consistent string handling and sorting
LOCALE_VAL = C
# Set UTC timezone for consistent time handling across builds
TZ_VAL = UTC
.PHONY: build-reproducible
build-reproducible: ## Build the reth binary into `target` directory with reproducible builds. Only works for x86_64-unknown-linux-gnu currently
# Extra RUSTFLAGS for reproducible builds. Can be overridden via the environment.
RUSTFLAGS_REPRODUCIBLE_EXTRA ?=
# `reproducible` only supports reth on x86_64-unknown-linux-gnu
build-%-reproducible:
@if [ "$*" != "reth" ]; then \
echo "Error: Reproducible builds are only supported for reth, not $*"; \
exit 1; \
fi
SOURCE_DATE_EPOCH=$(SOURCE_DATE) \
RUSTFLAGS="${RUST_BUILD_FLAGS} --remap-path-prefix $$(pwd)=." \
CARGO_INCREMENTAL=${CARGO_INCREMENTAL_VAL} \
LC_ALL=${LOCALE_VAL} \
TZ=${TZ_VAL} \
cargo build --bin reth --features "$(FEATURES)" --profile "release" --locked --target x86_64-unknown-linux-gnu
RUSTFLAGS="-C symbol-mangling-version=v0 -C strip=none -C link-arg=-Wl,--build-id=none -C metadata='' --remap-path-prefix $$(pwd)=. $(RUSTFLAGS_REPRODUCIBLE_EXTRA)" \
LC_ALL=C \
TZ=UTC \
JEMALLOC_OVERRIDE=/usr/lib/x86_64-linux-gnu/libjemalloc.a \
cargo build --bin reth --features "$(FEATURES) jemalloc-unprefixed" --profile "reproducible" --locked --target x86_64-unknown-linux-gnu
.PHONY: build-debug
build-debug: ## Build the reth binary into `target/debug` directory.
cargo build --bin reth --features "$(FEATURES)"
.PHONY: build-debug-op
build-debug-op: ## Build the op-reth binary into `target/debug` directory.
cargo build --bin op-reth --features "$(FEATURES)" --manifest-path crates/optimism/bin/Cargo.toml
.PHONY: build-op
build-op: ## Build the op-reth binary into `target` directory.
@@ -155,6 +149,22 @@ op-build-x86_64-apple-darwin:
op-build-aarch64-apple-darwin:
$(MAKE) op-build-native-aarch64-apple-darwin
build-deb-%:
@case "$*" in \
x86_64-unknown-linux-gnu|aarch64-unknown-linux-gnu|riscv64gc-unknown-linux-gnu) \
echo "Building debian package for $*"; \
;; \
*) \
echo "Error: Debian packages are only supported for x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, and riscv64gc-unknown-linux-gnu, not $*"; \
exit 1; \
;; \
esac
cargo install cargo-deb@3.6.0 --locked
cargo deb --profile $(PROFILE) --no-build --no-dbgsym --no-strip \
--target $* \
$(if $(VERSION),--deb-version "1~$(VERSION)") \
$(if $(VERSION),--output "target/$*/$(PROFILE)/reth-$(VERSION)-$*-$(PROFILE).deb")
# Create a `.tar.gz` containing a binary for a specific target.
define tarball_release_binary
cp $(CARGO_TARGET_DIR)/$(1)/$(PROFILE)/$(2) $(BIN_DIR)/$(2)
@@ -380,9 +390,9 @@ db-tools: ## Compile MDBX debugging tools.
@echo "Run \"$(DB_TOOLS_DIR)/mdbx_chk\" for the MDBX db file integrity check."
.PHONY: update-book-cli
update-book-cli: build-debug ## Update book cli documentation.
update-book-cli: build-debug build-debug-op## Update book cli documentation.
@echo "Updating book cli doc..."
@./docs/cli/update.sh $(CARGO_TARGET_DIR)/debug/reth
@./docs/cli/update.sh $(CARGO_TARGET_DIR)/debug/reth $(CARGO_TARGET_DIR)/debug/op-reth
.PHONY: profiling
profiling: ## Builds `reth` with optimisations, but also symbols.
@@ -511,10 +521,3 @@ pr:
make update-book-cli && \
cargo docs --document-private-items && \
make test
check-features:
cargo hack check \
--package reth-codecs \
--package reth-primitives-traits \
--package reth-primitives \
--feature-powerset

View File

@@ -103,7 +103,8 @@ impl BenchmarkRunner {
cmd.args(["--wait-time", wait_time]);
}
cmd.stdout(std::process::Stdio::piped())
cmd.env("RUST_LOG_STYLE", "never")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.kill_on_drop(true);
@@ -190,7 +191,8 @@ impl BenchmarkRunner {
cmd.args(["--wait-time", wait_time]);
}
cmd.stdout(std::process::Stdio::piped())
cmd.env("RUST_LOG_STYLE", "never")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.kill_on_drop(true);

View File

@@ -5,7 +5,7 @@ use clap::Parser;
use eyre::{eyre, Result, WrapErr};
use reth_chainspec::Chain;
use reth_cli_runner::CliContext;
use reth_node_core::args::{DatadirArgs, LogArgs};
use reth_node_core::args::{DatadirArgs, LogArgs, TraceArgs};
use reth_tracing::FileWorkerGuard;
use std::{net::TcpListener, path::PathBuf, str::FromStr};
use tokio::process::Command;
@@ -131,6 +131,19 @@ pub(crate) struct Args {
#[command(flatten)]
pub logs: LogArgs,
#[command(flatten)]
pub traces: TraceArgs,
/// Maximum queue size for OTLP Batch Span Processor (traces).
/// Higher values prevent trace drops when benchmarking many blocks.
#[arg(
long,
value_name = "OTLP_BUFFER_SIZE",
default_value = "32768",
help_heading = "Tracing"
)]
pub otlp_max_queue_size: usize,
/// Additional arguments to pass to baseline reth node command
///
/// Example: `--baseline-args "--debug.tip 0xabc..."`
@@ -493,8 +506,8 @@ async fn run_warmup_phase(
// Build additional args with conditional --debug.startup-sync-state-idle flag
let additional_args = args.build_additional_args("warmup", args.baseline_args.as_ref());
// Start reth node for warmup
let mut node_process =
// Start reth node for warmup (command is not stored for warmup phase)
let (mut node_process, _warmup_command) =
node_manager.start_node(&binary_path, warmup_ref, "warmup", &additional_args).await?;
// Wait for node to be ready and get its current tip
@@ -594,8 +607,8 @@ async fn run_benchmark_workflow(
// Build additional args with conditional --debug.startup-sync-state-idle flag
let additional_args = args.build_additional_args(ref_type, base_args_str);
// Start reth node
let mut node_process =
// Start reth node and capture the command for reporting
let (mut node_process, reth_command) =
node_manager.start_node(&binary_path, git_ref, ref_type, &additional_args).await?;
// Wait for node to be ready and get its current tip (wherever it is)
@@ -632,8 +645,9 @@ async fn run_benchmark_workflow(
// Store results for comparison
comparison_generator.add_ref_results(ref_type, &output_dir)?;
// Set the benchmark run timestamps
// Set the benchmark run timestamps and reth command
comparison_generator.set_ref_timestamps(ref_type, benchmark_start, benchmark_end)?;
comparison_generator.set_ref_command(ref_type, reth_command)?;
info!("Completed {} reference benchmark", ref_type);
}

View File

@@ -6,6 +6,7 @@ use csv::Reader;
use eyre::{eyre, Result, WrapErr};
use serde::{Deserialize, Serialize};
use std::{
cmp::Ordering,
collections::HashMap,
fs,
path::{Path, PathBuf},
@@ -20,6 +21,8 @@ pub(crate) struct ComparisonGenerator {
feature_ref_name: String,
baseline_results: Option<BenchmarkResults>,
feature_results: Option<BenchmarkResults>,
baseline_command: Option<String>,
feature_command: Option<String>,
}
/// Represents the results from a single benchmark run
@@ -36,6 +39,7 @@ pub(crate) struct BenchmarkResults {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub(crate) struct CombinedLatencyRow {
pub block_number: u64,
pub transaction_count: u64,
pub gas_used: u64,
pub new_payload_latency: u128,
}
@@ -44,19 +48,30 @@ pub(crate) struct CombinedLatencyRow {
#[derive(Debug, Clone, Deserialize, Serialize)]
pub(crate) struct TotalGasRow {
pub block_number: u64,
pub transaction_count: u64,
pub gas_used: u64,
pub time: u128,
}
/// Summary statistics for a benchmark run
/// Summary statistics for a benchmark run.
///
/// Latencies are derived from per-block `engine_newPayload` timings (converted from µs to ms):
/// - `mean_new_payload_latency_ms`: arithmetic mean latency across blocks.
/// - `median_new_payload_latency_ms`: p50 latency across blocks.
/// - `p90_new_payload_latency_ms` / `p99_new_payload_latency_ms`: tail latencies across blocks.
#[derive(Debug, Clone, Serialize)]
pub(crate) struct BenchmarkSummary {
pub total_blocks: u64,
pub total_gas_used: u64,
pub total_duration_ms: u128,
pub avg_new_payload_latency_ms: f64,
pub mean_new_payload_latency_ms: f64,
pub median_new_payload_latency_ms: f64,
pub p90_new_payload_latency_ms: f64,
pub p99_new_payload_latency_ms: f64,
pub gas_per_second: f64,
pub blocks_per_second: f64,
pub min_block_number: u64,
pub max_block_number: u64,
}
/// Comparison report between two benchmark runs
@@ -76,12 +91,32 @@ pub(crate) struct RefInfo {
pub summary: BenchmarkSummary,
pub start_timestamp: Option<DateTime<Utc>>,
pub end_timestamp: Option<DateTime<Utc>>,
pub reth_command: Option<String>,
}
/// Summary of the comparison between references
/// Summary of the comparison between references.
///
/// Percent deltas are `(feature - baseline) / baseline * 100`:
/// - `new_payload_latency_p50_change_percent` / p90 / p99: percent changes of the respective
/// per-block percentiles.
/// - `per_block_latency_change_mean_percent` / `per_block_latency_change_median_percent` are the
/// mean and median of per-block percent deltas (feature vs baseline), capturing block-level
/// drift.
/// - `per_block_latency_change_std_dev_percent`: standard deviation of per-block percent changes,
/// measuring consistency of performance changes across blocks.
/// - `new_payload_total_latency_change_percent` is the percent change of the total newPayload time
/// across the run.
///
/// Positive means slower/higher; negative means faster/lower.
#[derive(Debug, Serialize)]
pub(crate) struct ComparisonSummary {
pub new_payload_latency_change_percent: f64,
pub per_block_latency_change_mean_percent: f64,
pub per_block_latency_change_median_percent: f64,
pub per_block_latency_change_std_dev_percent: f64,
pub new_payload_total_latency_change_percent: f64,
pub new_payload_latency_p50_change_percent: f64,
pub new_payload_latency_p90_change_percent: f64,
pub new_payload_latency_p99_change_percent: f64,
pub gas_per_second_change_percent: f64,
pub blocks_per_second_change_percent: f64,
}
@@ -90,6 +125,8 @@ pub(crate) struct ComparisonSummary {
#[derive(Debug, Serialize)]
pub(crate) struct BlockComparison {
pub block_number: u64,
pub transaction_count: u64,
pub gas_used: u64,
pub baseline_new_payload_latency: u128,
pub feature_new_payload_latency: u128,
pub new_payload_latency_change_percent: f64,
@@ -108,6 +145,8 @@ impl ComparisonGenerator {
feature_ref_name: args.feature_ref.clone(),
baseline_results: None,
feature_results: None,
baseline_command: None,
feature_command: None,
}
}
@@ -172,6 +211,21 @@ impl ComparisonGenerator {
Ok(())
}
/// Set the reth command for a reference
pub(crate) fn set_ref_command(&mut self, ref_type: &str, command: String) -> Result<()> {
match ref_type {
"baseline" => {
self.baseline_command = Some(command);
}
"feature" => {
self.feature_command = Some(command);
}
_ => return Err(eyre!("Unknown reference type: {}", ref_type)),
}
Ok(())
}
/// Generate the final comparison report
pub(crate) async fn generate_comparison_report(&self) -> Result<()> {
info!("Generating comparison report...");
@@ -182,10 +236,12 @@ impl ComparisonGenerator {
let feature =
self.feature_results.as_ref().ok_or_else(|| eyre!("Feature results not loaded"))?;
// Generate comparison
let comparison_summary =
self.calculate_comparison_summary(&baseline.summary, &feature.summary)?;
let per_block_comparisons = self.calculate_per_block_comparisons(baseline, feature)?;
let comparison_summary = self.calculate_comparison_summary(
&baseline.summary,
&feature.summary,
&per_block_comparisons,
)?;
let report = ComparisonReport {
timestamp: self.timestamp.clone(),
@@ -194,12 +250,14 @@ impl ComparisonGenerator {
summary: baseline.summary.clone(),
start_timestamp: baseline.start_timestamp,
end_timestamp: baseline.end_timestamp,
reth_command: self.baseline_command.clone(),
},
feature: RefInfo {
ref_name: feature.ref_name.clone(),
summary: feature.summary.clone(),
start_timestamp: feature.start_timestamp,
end_timestamp: feature.end_timestamp,
reth_command: self.feature_command.clone(),
},
comparison_summary,
per_block_comparisons,
@@ -275,7 +333,11 @@ impl ComparisonGenerator {
Ok(rows)
}
/// Calculate summary statistics for a benchmark run
/// Calculate summary statistics for a benchmark run.
///
/// Computes latency statistics from per-block `new_payload_latency` values in `combined_data`
/// (converting from µs to ms), and throughput metrics using the total run duration from
/// `total_gas_data`. Percentiles (p50/p90/p99) use linear interpolation on sorted latencies.
fn calculate_summary(
&self,
combined_data: &[CombinedLatencyRow],
@@ -290,9 +352,16 @@ impl ComparisonGenerator {
let total_duration_ms = total_gas_data.last().unwrap().time / 1000; // Convert microseconds to milliseconds
let avg_new_payload_latency_ms: f64 =
combined_data.iter().map(|r| r.new_payload_latency as f64 / 1000.0).sum::<f64>() /
total_blocks as f64;
let latencies_ms: Vec<f64> =
combined_data.iter().map(|r| r.new_payload_latency as f64 / 1000.0).collect();
let mean_new_payload_latency_ms: f64 =
latencies_ms.iter().sum::<f64>() / total_blocks as f64;
let mut sorted_latencies_ms = latencies_ms;
sorted_latencies_ms.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
let median_new_payload_latency_ms = percentile(&sorted_latencies_ms, 0.5);
let p90_new_payload_latency_ms = percentile(&sorted_latencies_ms, 0.9);
let p99_new_payload_latency_ms = percentile(&sorted_latencies_ms, 0.99);
let total_duration_seconds = total_duration_ms as f64 / 1000.0;
let gas_per_second = if total_duration_seconds > f64::EPSILON {
@@ -307,13 +376,21 @@ impl ComparisonGenerator {
0.0
};
let min_block_number = combined_data.first().unwrap().block_number;
let max_block_number = combined_data.last().unwrap().block_number;
Ok(BenchmarkSummary {
total_blocks,
total_gas_used,
total_duration_ms,
avg_new_payload_latency_ms,
mean_new_payload_latency_ms,
median_new_payload_latency_ms,
p90_new_payload_latency_ms,
p99_new_payload_latency_ms,
gas_per_second,
blocks_per_second,
min_block_number,
max_block_number,
})
}
@@ -322,6 +399,7 @@ impl ComparisonGenerator {
&self,
baseline: &BenchmarkSummary,
feature: &BenchmarkSummary,
per_block_comparisons: &[BlockComparison],
) -> Result<ComparisonSummary> {
let calc_percent_change = |baseline: f64, feature: f64| -> f64 {
if baseline.abs() > f64::EPSILON {
@@ -331,10 +409,50 @@ impl ComparisonGenerator {
}
};
// Calculate per-block statistics. "Per-block" means: for each block, compute the percent
// change (feature - baseline) / baseline * 100, then calculate statistics across those
// per-block percent changes. This captures how consistently the feature performs relative
// to baseline across all blocks.
let per_block_percent_changes: Vec<f64> =
per_block_comparisons.iter().map(|c| c.new_payload_latency_change_percent).collect();
let per_block_latency_change_mean_percent = if per_block_percent_changes.is_empty() {
0.0
} else {
per_block_percent_changes.iter().sum::<f64>() / per_block_percent_changes.len() as f64
};
let per_block_latency_change_median_percent = if per_block_percent_changes.is_empty() {
0.0
} else {
let mut sorted = per_block_percent_changes.clone();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
percentile(&sorted, 0.5)
};
let per_block_latency_change_std_dev_percent =
calculate_std_dev(&per_block_percent_changes, per_block_latency_change_mean_percent);
let baseline_total_latency_ms =
baseline.mean_new_payload_latency_ms * baseline.total_blocks as f64;
let feature_total_latency_ms =
feature.mean_new_payload_latency_ms * feature.total_blocks as f64;
let new_payload_total_latency_change_percent =
calc_percent_change(baseline_total_latency_ms, feature_total_latency_ms);
Ok(ComparisonSummary {
new_payload_latency_change_percent: calc_percent_change(
baseline.avg_new_payload_latency_ms,
feature.avg_new_payload_latency_ms,
per_block_latency_change_mean_percent,
per_block_latency_change_median_percent,
per_block_latency_change_std_dev_percent,
new_payload_total_latency_change_percent,
new_payload_latency_p50_change_percent: calc_percent_change(
baseline.median_new_payload_latency_ms,
feature.median_new_payload_latency_ms,
),
new_payload_latency_p90_change_percent: calc_percent_change(
baseline.p90_new_payload_latency_ms,
feature.p90_new_payload_latency_ms,
),
new_payload_latency_p99_change_percent: calc_percent_change(
baseline.p99_new_payload_latency_ms,
feature.p99_new_payload_latency_ms,
),
gas_per_second_change_percent: calc_percent_change(
baseline.gas_per_second,
@@ -371,6 +489,8 @@ impl ComparisonGenerator {
let comparison = BlockComparison {
block_number: feature_row.block_number,
transaction_count: feature_row.transaction_count,
gas_used: feature_row.gas_used,
baseline_new_payload_latency: baseline_row.new_payload_latency,
feature_new_payload_latency: feature_row.new_payload_latency,
new_payload_latency_change_percent: calc_percent_change(
@@ -436,20 +556,62 @@ impl ComparisonGenerator {
let summary = &report.comparison_summary;
println!("Performance Changes:");
println!(" NewPayload Latency: {:+.2}%", summary.new_payload_latency_change_percent);
println!(" Gas/Second: {:+.2}%", summary.gas_per_second_change_percent);
println!(" Blocks/Second: {:+.2}%", summary.blocks_per_second_change_percent);
println!(
" NewPayload Latency per-block mean change: {:+.2}%",
summary.per_block_latency_change_mean_percent
);
println!(
" NewPayload Latency per-block median change: {:+.2}%",
summary.per_block_latency_change_median_percent
);
println!(
" NewPayload Latency per-block std dev: {:.2}%",
summary.per_block_latency_change_std_dev_percent
);
println!(
" Total newPayload time change: {:+.2}%",
summary.new_payload_total_latency_change_percent
);
println!(
" NewPayload Latency p50: {:+.2}%",
summary.new_payload_latency_p50_change_percent
);
println!(
" NewPayload Latency p90: {:+.2}%",
summary.new_payload_latency_p90_change_percent
);
println!(
" NewPayload Latency p99: {:+.2}%",
summary.new_payload_latency_p99_change_percent
);
println!(
" Gas/Second: {:+.2}%",
summary.gas_per_second_change_percent
);
println!(
" Blocks/Second: {:+.2}%",
summary.blocks_per_second_change_percent
);
println!();
println!("Baseline Summary:");
let baseline = &report.baseline.summary;
println!(
" Blocks: {}, Gas: {}, Duration: {:.2}s",
" Blocks: {} (blocks {} to {}), Gas: {}, Duration: {:.2}s",
baseline.total_blocks,
baseline.min_block_number,
baseline.max_block_number,
baseline.total_gas_used,
baseline.total_duration_ms as f64 / 1000.0
);
println!(" Avg NewPayload: {:.2}ms", baseline.avg_new_payload_latency_ms);
println!(" NewPayload latency (ms):");
println!(
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}",
baseline.mean_new_payload_latency_ms,
baseline.median_new_payload_latency_ms,
baseline.p90_new_payload_latency_ms,
baseline.p99_new_payload_latency_ms
);
if let (Some(start), Some(end)) =
(&report.baseline.start_timestamp, &report.baseline.end_timestamp)
{
@@ -459,17 +621,29 @@ impl ComparisonGenerator {
end.format("%Y-%m-%d %H:%M:%S UTC")
);
}
if let Some(ref cmd) = report.baseline.reth_command {
println!(" Command: {}", cmd);
}
println!();
println!("Feature Summary:");
let feature = &report.feature.summary;
println!(
" Blocks: {}, Gas: {}, Duration: {:.2}s",
" Blocks: {} (blocks {} to {}), Gas: {}, Duration: {:.2}s",
feature.total_blocks,
feature.min_block_number,
feature.max_block_number,
feature.total_gas_used,
feature.total_duration_ms as f64 / 1000.0
);
println!(" Avg NewPayload: {:.2}ms", feature.avg_new_payload_latency_ms);
println!(" NewPayload latency (ms):");
println!(
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}",
feature.mean_new_payload_latency_ms,
feature.median_new_payload_latency_ms,
feature.p90_new_payload_latency_ms,
feature.p99_new_payload_latency_ms
);
if let (Some(start), Some(end)) =
(&report.feature.start_timestamp, &report.feature.end_timestamp)
{
@@ -479,6 +653,58 @@ impl ComparisonGenerator {
end.format("%Y-%m-%d %H:%M:%S UTC")
);
}
if let Some(ref cmd) = report.feature.reth_command {
println!(" Command: {}", cmd);
}
println!();
}
}
/// Calculate standard deviation from a set of values and their mean.
///
/// Computes the population standard deviation using the formula:
/// `sqrt(sum((x - mean)²) / n)`
///
/// Returns 0.0 for empty input.
fn calculate_std_dev(values: &[f64], mean: f64) -> f64 {
if values.is_empty() {
return 0.0;
}
let variance = values
.iter()
.map(|x| {
let diff = x - mean;
diff * diff
})
.sum::<f64>() /
values.len() as f64;
variance.sqrt()
}
/// Calculate percentile using linear interpolation on a sorted slice.
///
/// Computes `rank = percentile × (n - 1)` where n is the array length. If the rank falls
/// between two indices, linearly interpolates between those values. For example, with 100 values,
/// p90 computes rank = 0.9 × 99 = 89.1, then returns `values[89] × 0.9 + values[90] × 0.1`.
///
/// Returns 0.0 for empty input.
fn percentile(sorted_values: &[f64], percentile: f64) -> f64 {
if sorted_values.is_empty() {
return 0.0;
}
let clamped = percentile.clamp(0.0, 1.0);
let max_index = sorted_values.len() - 1;
let rank = clamped * max_index as f64;
let lower = rank.floor() as usize;
let upper = rank.ceil() as usize;
if lower == upper {
sorted_values[lower]
} else {
let weight = rank - lower as f64;
sorted_values[lower].mul_add(1.0 - weight, sorted_values[upper] * weight)
}
}

View File

@@ -181,45 +181,43 @@ impl GitManager {
/// Validate that the specified git references exist (branches, tags, or commits)
pub(crate) fn validate_refs(&self, refs: &[&str]) -> Result<()> {
for &git_ref in refs {
// Try branch first, then tag, then commit
let branch_check = Command::new("git")
.args(["rev-parse", "--verify", &format!("refs/heads/{git_ref}")])
// Try to resolve the ref similar to `git checkout` by peeling to a commit.
// First try the ref as-is with ^{commit}, then fall back to origin/{ref}^{commit}.
let as_is = format!("{git_ref}^{{commit}}");
let ref_check = Command::new("git")
.args(["rev-parse", "--verify", &as_is])
.current_dir(&self.repo_root)
.output();
let tag_check = Command::new("git")
.args(["rev-parse", "--verify", &format!("refs/tags/{git_ref}")])
.current_dir(&self.repo_root)
.output();
let commit_check = Command::new("git")
.args(["rev-parse", "--verify", &format!("{git_ref}^{{commit}}")])
.current_dir(&self.repo_root)
.output();
let found = if let Ok(output) = branch_check &&
let found = if let Ok(output) = ref_check &&
output.status.success()
{
info!("Validated branch exists: {}", git_ref);
true
} else if let Ok(output) = tag_check &&
output.status.success()
{
info!("Validated tag exists: {}", git_ref);
true
} else if let Ok(output) = commit_check &&
output.status.success()
{
info!("Validated commit exists: {}", git_ref);
info!("Validated reference exists: {}", git_ref);
true
} else {
false
// Try remote-only branches via origin/{ref}
let origin_ref = format!("origin/{git_ref}^{{commit}}");
let origin_check = Command::new("git")
.args(["rev-parse", "--verify", &origin_ref])
.current_dir(&self.repo_root)
.output();
if let Ok(output) = origin_check &&
output.status.success()
{
info!("Validated remote reference exists: origin/{}", git_ref);
true
} else {
false
}
};
if !found {
return Err(eyre!(
"Git reference '{}' does not exist as branch, tag, or commit",
git_ref
"Git reference '{}' does not exist as branch, tag, or commit (tried '{}' and 'origin/{}^{{commit}}')",
git_ref,
format!("{git_ref}^{{commit}}"),
git_ref,
));
}
}

View File

@@ -29,6 +29,8 @@ pub(crate) struct NodeManager {
output_dir: PathBuf,
additional_reth_args: Vec<String>,
comparison_dir: Option<PathBuf>,
tracing_endpoint: Option<String>,
otlp_max_queue_size: usize,
}
impl NodeManager {
@@ -42,8 +44,16 @@ impl NodeManager {
binary_path: None,
enable_profiling: args.profile,
output_dir: args.output_dir_path(),
additional_reth_args: args.reth_args.clone(),
// Filter out empty strings to prevent invalid arguments being passed to reth node
additional_reth_args: args
.reth_args
.iter()
.filter(|s| !s.is_empty())
.cloned()
.collect(),
comparison_dir: None,
tracing_endpoint: args.traces.otlp.as_ref().map(|u| u.to_string()),
otlp_max_queue_size: args.otlp_max_queue_size,
}
}
@@ -119,6 +129,7 @@ impl NodeManager {
&self,
binary_path_str: &str,
additional_args: &[String],
ref_type: &str,
) -> (Vec<String>, String) {
let mut reth_args = vec![binary_path_str.to_string(), "node".to_string()];
@@ -146,6 +157,13 @@ impl NodeManager {
"--trusted-only".to_string(),
]);
// Add tracing arguments if OTLP endpoint is configured
if let Some(ref endpoint) = self.tracing_endpoint {
info!("Enabling OTLP tracing export to: {} (service: reth-{})", endpoint, ref_type);
// Endpoint requires equals per clap settings in reth
reth_args.push(format!("--tracing-otlp={}", endpoint));
}
// Add any additional arguments passed via command line (common to both baseline and
// feature)
reth_args.extend_from_slice(&self.additional_reth_args);
@@ -193,6 +211,9 @@ impl NodeManager {
cmd.arg("--");
cmd.args(reth_args);
// Set environment variable to disable log styling
cmd.env("RUST_LOG_STYLE", "never");
Ok(cmd)
}
@@ -200,32 +221,42 @@ impl NodeManager {
fn create_direct_command(&self, reth_args: &[String]) -> Command {
let binary_path = &reth_args[0];
if self.use_sudo {
let mut cmd = if self.use_sudo {
info!("Starting reth node with sudo...");
let mut cmd = Command::new("sudo");
cmd.args(reth_args);
cmd
let mut sudo_cmd = Command::new("sudo");
sudo_cmd.args(reth_args);
sudo_cmd
} else {
info!("Starting reth node...");
let mut cmd = Command::new(binary_path);
cmd.args(&reth_args[1..]); // Skip the binary path since it's the command
cmd
}
let mut reth_cmd = Command::new(binary_path);
reth_cmd.args(&reth_args[1..]); // Skip the binary path since it's the command
reth_cmd
};
// Set environment variable to disable log styling
cmd.env("RUST_LOG_STYLE", "never");
cmd
}
/// Start a reth node using the specified binary path and return the process handle
/// along with the formatted reth command string for reporting.
pub(crate) async fn start_node(
&mut self,
binary_path: &std::path::Path,
_git_ref: &str,
ref_type: &str,
additional_args: &[String],
) -> Result<tokio::process::Child> {
) -> Result<(tokio::process::Child, String)> {
// Store the binary path for later use (e.g., in unwind_to_block)
self.binary_path = Some(binary_path.to_path_buf());
let binary_path_str = binary_path.to_string_lossy();
let (reth_args, _) = self.build_reth_args(&binary_path_str, additional_args);
let (reth_args, _) = self.build_reth_args(&binary_path_str, additional_args, ref_type);
// Format the reth command string for reporting
let reth_command = shlex::try_join(reth_args.iter().map(|s| s.as_str()))
.wrap_err("Failed to format reth command string")?;
// Log additional arguments if any
if !self.additional_reth_args.is_empty() {
@@ -247,6 +278,15 @@ impl NodeManager {
cmd.process_group(0);
}
// Set high queue size to prevent trace dropping during benchmarks
if self.tracing_endpoint.is_some() {
cmd.env("OTEL_BSP_MAX_QUEUE_SIZE", self.otlp_max_queue_size.to_string()); // Traces
cmd.env("OTEL_BLRP_MAX_QUEUE_SIZE", "10000"); // Logs
// Set service name to differentiate baseline vs feature runs in Jaeger
cmd.env("OTEL_SERVICE_NAME", format!("reth-{}", ref_type));
}
debug!("Executing reth command: {cmd:?}");
let mut child = cmd
@@ -311,7 +351,7 @@ impl NodeManager {
// Give the node a moment to start up
sleep(Duration::from_secs(5)).await;
Ok(child)
Ok((child, reth_command))
}
/// Wait for the node to be ready and return its current tip
@@ -468,6 +508,9 @@ impl NodeManager {
cmd.args(["to-block", &block_number.to_string()]);
// Set environment variable to disable log styling
cmd.env("RUST_LOG_STYLE", "never");
// Debug log the command
debug!("Executing reth unwind command: {:?}", cmd);

View File

@@ -80,7 +80,7 @@ RUSTFLAGS="-C target-cpu=native" cargo build --profile profiling --no-default-fe
### Run the Benchmark:
First, start the reth node. Here is an example that runs `reth` compiled with the `profiling` profile, runs `samply`, and configures `reth` to run with metrics enabled:
```bash
samply record -p 3001 target/profiling/reth node --metrics localhost:9001 --authrpc.jwt-secret <jwt_file_path>
samply record -p 3001 target/profiling/reth node --metrics localhost:9001 --authrpc.jwtsecret <jwt_file_path>
```
```bash
@@ -143,5 +143,5 @@ To reproduce the benchmark, first re-set the node to the block that the benchmar
- **RPC Configuration**: The RPC endpoints should be accessible and configured correctly, specifically the RPC endpoint must support `eth_getBlockByNumber` and support fetching full transactions. The benchmark will make one RPC query per block as fast as possible, so ensure the RPC endpoint does not rate limit or block requests after a certain volume.
- **Reproducibility**: Ensure that the node is at the same state before attempting to retry a benchmark. The `new-payload-fcu` command specifically will commit to the database, so the node must be rolled back using `reth stage unwind` to reproducibly retry benchmarks.
- **Profiling tools**: If you are collecting CPU profiles, tools like [`samply`](https://github.com/mstange/samply) and [`perf`](https://perf.wiki.kernel.org/index.php/Main_Page) can be useful for analyzing node performance.
- **Benchmark Data**: `reth-bench` additionally contains a `--benchmark.output` flag, which will output gas used benchmarks across the benchmark range in CSV format. This may be useful for further data analysis.
- **Benchmark Data**: `reth-bench` additionally contains a `--output` flag, which will output gas used benchmarks across the benchmark range in CSV format. This may be useful for further data analysis.
- **Platform Information**: To ensure accurate and reproducible benchmarking, document the platform details, including hardware specifications, OS version, and any other relevant information before publishing any benchmarks.

View File

@@ -79,22 +79,13 @@ impl Command {
break;
}
};
let header = block.header.clone();
let (version, params) = match block_to_new_payload(block, is_optimism) {
Ok(result) => result,
Err(e) => {
tracing::error!("Failed to convert block to new payload: {e}");
let _ = error_sender.send(e);
break;
}
};
let head_block_hash = header.hash;
let safe_block_hash =
block_provider.get_block_by_number(header.number.saturating_sub(32).into());
let head_block_hash = block.header.hash;
let safe_block_hash = block_provider
.get_block_by_number(block.header.number.saturating_sub(32).into());
let finalized_block_hash =
block_provider.get_block_by_number(header.number.saturating_sub(64).into());
let finalized_block_hash = block_provider
.get_block_by_number(block.header.number.saturating_sub(64).into());
let (safe, finalized) = tokio::join!(safe_block_hash, finalized_block_hash,);
@@ -110,14 +101,7 @@ impl Command {
next_block += 1;
if let Err(e) = sender
.send((
header,
version,
params,
head_block_hash,
safe_block_hash,
finalized_block_hash,
))
.send((block, head_block_hash, safe_block_hash, finalized_block_hash))
.await
{
tracing::error!("Failed to send block data: {e}");
@@ -131,15 +115,16 @@ impl Command {
let total_benchmark_duration = Instant::now();
let mut total_wait_time = Duration::ZERO;
while let Some((header, version, params, head, safe, finalized)) = {
while let Some((block, head, safe, finalized)) = {
let wait_start = Instant::now();
let result = receiver.recv().await;
total_wait_time += wait_start.elapsed();
result
} {
// just put gas used here
let gas_used = header.gas_used;
let block_number = header.number;
let gas_used = block.header.gas_used;
let block_number = block.header.number;
let transaction_count = block.transactions.len() as u64;
debug!(target: "reth-bench", ?block_number, "Sending payload",);
@@ -150,6 +135,7 @@ impl Command {
finalized_block_hash: finalized,
};
let (version, params) = block_to_new_payload(block, is_optimism)?;
let start = Instant::now();
call_new_payload(&auth_provider, version, params).await?;
@@ -160,8 +146,13 @@ impl Command {
// calculate the total duration and the fcu latency, record
let total_latency = start.elapsed();
let fcu_latency = total_latency - new_payload_result.latency;
let combined_result =
CombinedResult { block_number, new_payload_result, fcu_latency, total_latency };
let combined_result = CombinedResult {
block_number,
transaction_count,
new_payload_result,
fcu_latency,
total_latency,
};
// current duration since the start of the benchmark minus the time
// waiting for blocks
@@ -174,7 +165,8 @@ impl Command {
tokio::time::sleep(self.wait_time).await;
// record the current result
let gas_row = TotalGasRow { block_number, gas_used, time: current_duration };
let gas_row =
TotalGasRow { block_number, transaction_count, gas_used, time: current_duration };
results.push((gas_row, combined_result));
}

View File

@@ -72,19 +72,9 @@ impl Command {
break;
}
};
let header = block.header.clone();
let (version, params) = match block_to_new_payload(block, is_optimism) {
Ok(result) => result,
Err(e) => {
tracing::error!("Failed to convert block to new payload: {e}");
let _ = error_sender.send(e);
break;
}
};
next_block += 1;
if let Err(e) = sender.send((header, version, params)).await {
if let Err(e) = sender.send(block).await {
tracing::error!("Failed to send block data: {e}");
break;
}
@@ -96,23 +86,24 @@ impl Command {
let total_benchmark_duration = Instant::now();
let mut total_wait_time = Duration::ZERO;
while let Some((header, version, params)) = {
while let Some(block) = {
let wait_start = Instant::now();
let result = receiver.recv().await;
total_wait_time += wait_start.elapsed();
result
} {
// just put gas used here
let gas_used = header.gas_used;
let block_number = header.number;
let block_number = block.header.number;
let transaction_count = block.transactions.len() as u64;
let gas_used = block.header.gas_used;
debug!(
target: "reth-bench",
number=?header.number,
number=?block.header.number,
"Sending payload to engine",
);
let (version, params) = block_to_new_payload(block, is_optimism)?;
let start = Instant::now();
call_new_payload(&auth_provider, version, params).await?;
@@ -124,7 +115,8 @@ impl Command {
let current_duration = total_benchmark_duration.elapsed() - total_wait_time;
// record the current result
let row = TotalGasRow { block_number, gas_used, time: current_duration };
let row =
TotalGasRow { block_number, transaction_count, gas_used, time: current_duration };
results.push((row, new_payload_result));
}

View File

@@ -67,6 +67,8 @@ impl Serialize for NewPayloadResult {
pub(crate) struct CombinedResult {
/// The block number of the block being processed.
pub(crate) block_number: u64,
/// The number of transactions in the block.
pub(crate) transaction_count: u64,
/// The `newPayload` result.
pub(crate) new_payload_result: NewPayloadResult,
/// The latency of the `forkchoiceUpdated` call.
@@ -108,10 +110,11 @@ impl Serialize for CombinedResult {
let fcu_latency = self.fcu_latency.as_micros();
let new_payload_latency = self.new_payload_result.latency.as_micros();
let total_latency = self.total_latency.as_micros();
let mut state = serializer.serialize_struct("CombinedResult", 5)?;
let mut state = serializer.serialize_struct("CombinedResult", 6)?;
// flatten the new payload result because this is meant for CSV writing
state.serialize_field("block_number", &self.block_number)?;
state.serialize_field("transaction_count", &self.transaction_count)?;
state.serialize_field("gas_used", &self.new_payload_result.gas_used)?;
state.serialize_field("new_payload_latency", &new_payload_latency)?;
state.serialize_field("fcu_latency", &fcu_latency)?;
@@ -125,6 +128,8 @@ impl Serialize for CombinedResult {
pub(crate) struct TotalGasRow {
/// The block number of the block being processed.
pub(crate) block_number: u64,
/// The number of transactions in the block.
pub(crate) transaction_count: u64,
/// The total gas used in the block.
pub(crate) gas_used: u64,
/// Time since the start of the benchmark.
@@ -172,8 +177,9 @@ impl Serialize for TotalGasRow {
{
// convert the time to microseconds
let time = self.time.as_micros();
let mut state = serializer.serialize_struct("TotalGasRow", 3)?;
let mut state = serializer.serialize_struct("TotalGasRow", 4)?;
state.serialize_field("block_number", &self.block_number)?;
state.serialize_field("transaction_count", &self.transaction_count)?;
state.serialize_field("gas_used", &self.gas_used)?;
state.serialize_field("time", &time)?;
state.end()
@@ -188,7 +194,12 @@ mod tests {
#[test]
fn test_write_total_gas_row_csv() {
let row = TotalGasRow { block_number: 1, gas_used: 1_000, time: Duration::from_secs(1) };
let row = TotalGasRow {
block_number: 1,
transaction_count: 10,
gas_used: 1_000,
time: Duration::from_secs(1),
};
let mut writer = Writer::from_writer(vec![]);
writer.serialize(row).unwrap();
@@ -198,11 +209,11 @@ mod tests {
let mut result = result.as_slice().lines();
// assert header
let expected_first_line = "block_number,gas_used,time";
let expected_first_line = "block_number,transaction_count,gas_used,time";
let first_line = result.next().unwrap().unwrap();
assert_eq!(first_line, expected_first_line);
let expected_second_line = "1,1000,1000000";
let expected_second_line = "1,10,1000,1000000";
let second_line = result.next().unwrap().unwrap();
assert_eq!(second_line, expected_second_line);
}

View File

@@ -9,6 +9,20 @@ repository.workspace = true
description = "Reth node implementation"
default-run = "reth"
[package.metadata.deb]
maintainer = "reth team"
depends = "$auto"
section = "network"
priority = "optional"
maintainer-scripts = "../../pkg/reth/debian/"
assets = [
"$auto",
["../../README.md", "usr/share/doc/reth/", "644"],
["../../LICENSE-APACHE", "usr/share/doc/reth/", "644"],
["../../LICENSE-MIT", "usr/share/doc/reth/", "644"],
]
systemd-units = { enable = false, start = false, unit-name = "reth", unit-scripts = "../../pkg/reth/debian" }
[lints]
workspace = true
@@ -67,7 +81,7 @@ backon.workspace = true
tempfile.workspace = true
[features]
default = ["jemalloc", "otlp", "reth-revm/portable", "js-tracer"]
default = ["jemalloc", "otlp", "reth-revm/portable", "js-tracer", "keccak-cache-global", "asm-keccak"]
otlp = [
"reth-ethereum-cli/otlp",
@@ -88,7 +102,9 @@ asm-keccak = [
"reth-ethereum-cli/asm-keccak",
"reth-node-ethereum/asm-keccak",
]
keccak-cache-global = [
"reth-node-ethereum/keccak-cache-global",
]
jemalloc = [
"reth-cli-util/jemalloc",
"reth-node-core/jemalloc",
@@ -100,6 +116,12 @@ jemalloc-prof = [
"reth-cli-util/jemalloc-prof",
"reth-ethereum-cli/jemalloc-prof",
]
jemalloc-unprefixed = [
"reth-cli-util/jemalloc-unprefixed",
"reth-node-core/jemalloc",
"reth-node-metrics/jemalloc",
"reth-ethereum-cli/jemalloc",
]
tracy-allocator = [
"reth-cli-util/tracy-allocator",
"reth-ethereum-cli/tracy-allocator",

View File

@@ -0,0 +1,444 @@
use alloy_primitives::B256;
use parking_lot::Mutex;
use reth_metrics::{metrics::Counter, Metrics};
use reth_trie::{
updates::{TrieUpdates, TrieUpdatesSorted},
HashedPostState, HashedPostStateSorted, TrieInputSorted,
};
use std::{
fmt,
sync::{Arc, LazyLock},
};
use tracing::instrument;
/// Shared handle to asynchronously populated trie data.
///
/// Uses a try-lock + fallback computation approach for deadlock-free access.
/// If the deferred task hasn't completed, computes trie data synchronously
/// from stored unsorted inputs rather than blocking.
#[derive(Clone)]
pub struct DeferredTrieData {
/// Shared deferred state holding either raw inputs (pending) or computed result (ready).
state: Arc<Mutex<DeferredState>>,
}
/// Sorted trie data computed for an executed block.
/// These represent the complete set of sorted trie data required to persist
/// block state for, and generate proofs on top of, a block.
#[derive(Clone, Debug, Default)]
pub struct ComputedTrieData {
/// Sorted hashed post-state produced by execution.
pub hashed_state: Arc<HashedPostStateSorted>,
/// Sorted trie updates produced by state root computation.
pub trie_updates: Arc<TrieUpdatesSorted>,
/// Trie input bundled with its anchor hash, if available.
pub anchored_trie_input: Option<AnchoredTrieInput>,
}
/// Trie input bundled with its anchor hash.
///
/// This is used to store the trie input and anchor hash for a block together.
#[derive(Clone, Debug)]
pub struct AnchoredTrieInput {
/// The persisted ancestor hash this trie input is anchored to.
pub anchor_hash: B256,
/// Trie input constructed from in-memory overlays.
pub trie_input: Arc<TrieInputSorted>,
}
/// Metrics for deferred trie computation.
#[derive(Metrics)]
#[metrics(scope = "sync.block_validation")]
struct DeferredTrieMetrics {
/// Number of times deferred trie data was ready (async task completed first).
deferred_trie_async_ready: Counter,
/// Number of times deferred trie data required synchronous computation (fallback path).
deferred_trie_sync_fallback: Counter,
}
static DEFERRED_TRIE_METRICS: LazyLock<DeferredTrieMetrics> =
LazyLock::new(DeferredTrieMetrics::default);
/// Internal state for deferred trie data.
enum DeferredState {
/// Data is not yet available; raw inputs stored for fallback computation.
Pending(PendingInputs),
/// Data has been computed and is ready.
Ready(ComputedTrieData),
}
/// Inputs kept while a deferred trie computation is pending.
#[derive(Clone, Debug)]
struct PendingInputs {
/// Unsorted hashed post-state from execution.
hashed_state: Arc<HashedPostState>,
/// Unsorted trie updates from state root computation.
trie_updates: Arc<TrieUpdates>,
/// The persisted ancestor hash this trie input is anchored to.
anchor_hash: B256,
/// Deferred trie data from ancestor blocks for merging.
ancestors: Vec<DeferredTrieData>,
}
impl fmt::Debug for DeferredTrieData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let state = self.state.lock();
match &*state {
DeferredState::Pending(_) => {
f.debug_struct("DeferredTrieData").field("state", &"pending").finish()
}
DeferredState::Ready(_) => {
f.debug_struct("DeferredTrieData").field("state", &"ready").finish()
}
}
}
}
impl DeferredTrieData {
/// Create a new pending handle with fallback inputs for synchronous computation.
///
/// If the async task hasn't completed when `wait_cloned` is called, the trie data
/// will be computed synchronously from these inputs. This eliminates deadlock risk.
///
/// # Arguments
/// * `hashed_state` - Unsorted hashed post-state from execution
/// * `trie_updates` - Unsorted trie updates from state root computation
/// * `anchor_hash` - The persisted ancestor hash this trie input is anchored to
/// * `ancestors` - Deferred trie data from ancestor blocks for merging
pub fn pending(
hashed_state: Arc<HashedPostState>,
trie_updates: Arc<TrieUpdates>,
anchor_hash: B256,
ancestors: Vec<Self>,
) -> Self {
Self {
state: Arc::new(Mutex::new(DeferredState::Pending(PendingInputs {
hashed_state,
trie_updates,
anchor_hash,
ancestors,
}))),
}
}
/// Create a handle that is already populated with the given [`ComputedTrieData`].
///
/// Useful when trie data is available immediately.
/// [`Self::wait_cloned`] will return without any computation.
pub fn ready(bundle: ComputedTrieData) -> Self {
Self { state: Arc::new(Mutex::new(DeferredState::Ready(bundle))) }
}
/// Sort block execution outputs and build a [`TrieInputSorted`] overlay.
///
/// The trie input overlay accumulates sorted hashed state (account/storage changes) and
/// trie node updates from all in-memory ancestor blocks. This overlay is required for:
/// - Computing state roots on top of in-memory blocks
/// - Generating storage/account proofs for unpersisted state
///
/// # Process
/// 1. Sort the current block's hashed state and trie updates
/// 2. Merge ancestor overlays (oldest -> newest, so later state takes precedence)
/// 3. Extend the merged overlay with this block's sorted data
///
/// Used by both the async background task and the synchronous fallback path.
///
/// # Arguments
/// * `hashed_state` - Unsorted hashed post-state (account/storage changes) from execution
/// * `trie_updates` - Unsorted trie node updates from state root computation
/// * `anchor_hash` - The persisted ancestor hash this trie input is anchored to
/// * `ancestors` - Deferred trie data from ancestor blocks for merging
pub fn sort_and_build_trie_input(
hashed_state: &HashedPostState,
trie_updates: &TrieUpdates,
anchor_hash: B256,
ancestors: &[Self],
) -> ComputedTrieData {
// Sort the current block's hashed state and trie updates
let sorted_hashed_state = Arc::new(hashed_state.clone_into_sorted());
let sorted_trie_updates = Arc::new(trie_updates.clone().into_sorted());
// Merge trie data from ancestors (oldest -> newest so later state takes precedence)
let mut overlay = TrieInputSorted::default();
for ancestor in ancestors {
let ancestor_data = ancestor.wait_cloned();
{
let state_mut = Arc::make_mut(&mut overlay.state);
state_mut.extend_ref(ancestor_data.hashed_state.as_ref());
}
{
let nodes_mut = Arc::make_mut(&mut overlay.nodes);
nodes_mut.extend_ref(ancestor_data.trie_updates.as_ref());
}
}
// Extend overlay with current block's sorted data
{
let state_mut = Arc::make_mut(&mut overlay.state);
state_mut.extend_ref(sorted_hashed_state.as_ref());
}
{
let nodes_mut = Arc::make_mut(&mut overlay.nodes);
nodes_mut.extend_ref(sorted_trie_updates.as_ref());
}
ComputedTrieData::with_trie_input(
sorted_hashed_state,
sorted_trie_updates,
anchor_hash,
Arc::new(overlay),
)
}
/// Returns trie data, computing synchronously if the async task hasn't completed.
///
/// - If the async task has completed (`Ready`), returns the cached result.
/// - If pending, computes synchronously from stored inputs.
///
/// Deadlock is avoided as long as the provided ancestors form a true ancestor chain (a DAG):
/// - Each block only waits on its ancestors (blocks on the path to the persisted root)
/// - Sibling blocks (forks) are never in each other's ancestor lists
/// - A block never waits on its descendants
///
/// Given that invariant, circular wait dependencies are impossible.
#[instrument(level = "debug", target = "engine::tree::deferred_trie", skip_all)]
pub fn wait_cloned(&self) -> ComputedTrieData {
let mut state = self.state.lock();
match &*state {
// If the deferred trie data is ready, return the cached result.
DeferredState::Ready(bundle) => {
DEFERRED_TRIE_METRICS.deferred_trie_async_ready.increment(1);
bundle.clone()
}
// If the deferred trie data is pending, compute the trie data synchronously and return
// the result. This is the fallback path if the async task hasn't completed.
DeferredState::Pending(inputs) => {
DEFERRED_TRIE_METRICS.deferred_trie_sync_fallback.increment(1);
let computed = Self::sort_and_build_trie_input(
&inputs.hashed_state,
&inputs.trie_updates,
inputs.anchor_hash,
&inputs.ancestors,
);
*state = DeferredState::Ready(computed.clone());
computed
}
}
}
}
impl ComputedTrieData {
/// Construct a bundle that includes trie input anchored to a persisted ancestor.
pub const fn with_trie_input(
hashed_state: Arc<HashedPostStateSorted>,
trie_updates: Arc<TrieUpdatesSorted>,
anchor_hash: B256,
trie_input: Arc<TrieInputSorted>,
) -> Self {
Self {
hashed_state,
trie_updates,
anchored_trie_input: Some(AnchoredTrieInput { anchor_hash, trie_input }),
}
}
/// Construct a bundle without trie input or anchor information.
///
/// Unlike [`Self::with_trie_input`], this constructor omits the accumulated trie input overlay
/// and its anchor hash. Use this when the trie input is not needed, such as in block builders
/// or sequencers that don't require proof generation on top of in-memory state.
///
/// The trie input anchor identifies the persisted block hash from which the in-memory overlay
/// was built. Without it, consumers cannot determine which on-disk state to combine with.
pub const fn without_trie_input(
hashed_state: Arc<HashedPostStateSorted>,
trie_updates: Arc<TrieUpdatesSorted>,
) -> Self {
Self { hashed_state, trie_updates, anchored_trie_input: None }
}
/// Returns the anchor hash, if present.
pub fn anchor_hash(&self) -> Option<B256> {
self.anchored_trie_input.as_ref().map(|anchored| anchored.anchor_hash)
}
/// Returns the trie input, if present.
pub fn trie_input(&self) -> Option<&Arc<TrieInputSorted>> {
self.anchored_trie_input.as_ref().map(|anchored| &anchored.trie_input)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::{map::B256Map, U256};
use reth_primitives_traits::Account;
use reth_trie::updates::TrieUpdates;
use std::{
sync::Arc,
thread,
time::{Duration, Instant},
};
fn empty_bundle() -> ComputedTrieData {
ComputedTrieData {
hashed_state: Arc::default(),
trie_updates: Arc::default(),
anchored_trie_input: None,
}
}
fn empty_pending() -> DeferredTrieData {
empty_pending_with_anchor(B256::ZERO)
}
fn empty_pending_with_anchor(anchor: B256) -> DeferredTrieData {
DeferredTrieData::pending(
Arc::new(HashedPostState::default()),
Arc::new(TrieUpdates::default()),
anchor,
Vec::new(),
)
}
/// Verifies that a ready handle returns immediately without computation.
#[test]
fn ready_returns_immediately() {
let bundle = empty_bundle();
let deferred = DeferredTrieData::ready(bundle.clone());
let start = Instant::now();
let result = deferred.wait_cloned();
let elapsed = start.elapsed();
assert_eq!(result.hashed_state, bundle.hashed_state);
assert_eq!(result.trie_updates, bundle.trie_updates);
assert_eq!(result.anchor_hash(), bundle.anchor_hash());
assert!(elapsed < Duration::from_millis(20));
}
/// Verifies that a pending handle computes trie data synchronously via fallback.
#[test]
fn pending_computes_fallback() {
let deferred = empty_pending();
// wait_cloned should compute from inputs without blocking
let start = Instant::now();
let result = deferred.wait_cloned();
let elapsed = start.elapsed();
// Should return quickly (fallback computation)
assert!(elapsed < Duration::from_millis(100));
assert!(result.hashed_state.is_empty());
}
/// Verifies that fallback computation result is cached for subsequent calls.
#[test]
fn fallback_result_is_cached() {
let deferred = empty_pending();
// First call computes and should stash the result
let first = deferred.wait_cloned();
// Second call should reuse the cached result (same Arc pointer)
let second = deferred.wait_cloned();
assert!(Arc::ptr_eq(&first.hashed_state, &second.hashed_state));
assert!(Arc::ptr_eq(&first.trie_updates, &second.trie_updates));
assert_eq!(first.anchor_hash(), second.anchor_hash());
}
/// Verifies that concurrent `wait_cloned` calls result in only one computation,
/// with all callers receiving the same cached result.
#[test]
fn concurrent_wait_cloned_computes_once() {
let deferred = empty_pending();
// Spawn multiple threads that all call wait_cloned concurrently
let handles: Vec<_> = (0..10)
.map(|_| {
let d = deferred.clone();
thread::spawn(move || d.wait_cloned())
})
.collect();
// Collect all results
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
// All results should share the same Arc pointers (same computed result)
let first = &results[0];
for result in &results[1..] {
assert!(Arc::ptr_eq(&first.hashed_state, &result.hashed_state));
assert!(Arc::ptr_eq(&first.trie_updates, &result.trie_updates));
}
}
/// Tests that ancestor trie data is merged during fallback computation and that the
/// resulting `ComputedTrieData` uses the current block's anchor hash, not the ancestor's.
#[test]
fn ancestors_are_merged() {
// Create ancestor with some data
let ancestor_bundle = ComputedTrieData {
hashed_state: Arc::default(),
trie_updates: Arc::default(),
anchored_trie_input: Some(AnchoredTrieInput {
anchor_hash: B256::with_last_byte(1),
trie_input: Arc::new(TrieInputSorted::default()),
}),
};
let ancestor = DeferredTrieData::ready(ancestor_bundle);
// Create pending with ancestor
let deferred = DeferredTrieData::pending(
Arc::new(HashedPostState::default()),
Arc::new(TrieUpdates::default()),
B256::with_last_byte(2),
vec![ancestor],
);
let result = deferred.wait_cloned();
// Should have the current block's anchor, not the ancestor's
assert_eq!(result.anchor_hash(), Some(B256::with_last_byte(2)));
}
/// Ensures ancestor overlays are merged oldest -> newest so latest state wins (no overwrite by
/// older ancestors).
#[test]
fn ancestors_merge_in_chronological_order() {
let key = B256::with_last_byte(1);
// Oldest ancestor sets nonce to 1
let oldest_state = HashedPostStateSorted::new(
vec![(key, Some(Account { nonce: 1, balance: U256::ZERO, bytecode_hash: None }))],
B256Map::default(),
);
// Newest ancestor overwrites nonce to 2
let newest_state = HashedPostStateSorted::new(
vec![(key, Some(Account { nonce: 2, balance: U256::ZERO, bytecode_hash: None }))],
B256Map::default(),
);
let oldest = ComputedTrieData {
hashed_state: Arc::new(oldest_state),
trie_updates: Arc::default(),
anchored_trie_input: None,
};
let newest = ComputedTrieData {
hashed_state: Arc::new(newest_state),
trie_updates: Arc::default(),
anchored_trie_input: None,
};
// Pass ancestors oldest -> newest; newest should take precedence
let deferred = DeferredTrieData::pending(
Arc::new(HashedPostState::default()),
Arc::new(TrieUpdates::default()),
B256::ZERO,
vec![DeferredTrieData::ready(oldest), DeferredTrieData::ready(newest)],
);
let result = deferred.wait_cloned();
let overlay_state = &result.anchored_trie_input.as_ref().unwrap().trie_input.state.accounts;
assert_eq!(overlay_state.len(), 1);
let (_, account) = &overlay_state[0];
assert_eq!(account.unwrap().nonce, 2);
}
}

View File

@@ -2,7 +2,7 @@
use crate::{
CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications,
ChainInfoTracker, MemoryOverlayStateProvider,
ChainInfoTracker, ComputedTrieData, DeferredTrieData, MemoryOverlayStateProvider,
};
use alloy_consensus::{transaction::TransactionMeta, BlockHeader};
use alloy_eips::{BlockHashOrNumber, BlockNumHash};
@@ -17,8 +17,8 @@ use reth_primitives_traits::{
SignedTransaction,
};
use reth_storage_api::StateProviderBox;
use reth_trie::{updates::TrieUpdates, HashedPostState};
use std::{collections::BTreeMap, sync::Arc, time::Instant};
use reth_trie::{updates::TrieUpdatesSorted, HashedPostStateSorted, TrieInputSorted};
use std::{collections::BTreeMap, ops::Deref, sync::Arc, time::Instant};
use tokio::sync::{broadcast, watch};
/// Size of the broadcast channel used to notify canonical state events.
@@ -565,7 +565,7 @@ impl<N: NodePrimitives> CanonicalInMemoryState<N> {
/// State after applying the given block, this block is part of the canonical chain that partially
/// stored in memory and can be traced back to a canonical block on disk.
#[derive(Debug, PartialEq, Eq, Clone)]
#[derive(Debug, Clone)]
pub struct BlockState<N: NodePrimitives = EthPrimitives> {
/// The executed block that determines the state after this block has been executed.
block: ExecutedBlock<N>,
@@ -573,6 +573,12 @@ pub struct BlockState<N: NodePrimitives = EthPrimitives> {
parent: Option<Arc<Self>>,
}
impl<N: NodePrimitives> PartialEq for BlockState<N> {
fn eq(&self, other: &Self) -> bool {
self.block == other.block && self.parent == other.parent
}
}
impl<N: NodePrimitives> BlockState<N> {
/// [`BlockState`] constructor.
pub const fn new(block: ExecutedBlock<N>) -> Self {
@@ -628,6 +634,8 @@ impl<N: NodePrimitives> BlockState<N> {
/// We assume that the `Receipts` in the executed block `ExecutionOutcome`
/// has only one element corresponding to the executed block associated to
/// the state.
///
/// This clones the vector of receipts. To avoid it, use [`Self::executed_block_receipts_ref`].
pub fn executed_block_receipts(&self) -> Vec<N::Receipt> {
let receipts = self.receipts();
@@ -640,6 +648,22 @@ impl<N: NodePrimitives> BlockState<N> {
receipts.first().cloned().unwrap_or_default()
}
/// Returns a slice of `Receipt` of executed block that determines the state.
/// We assume that the `Receipts` in the executed block `ExecutionOutcome`
/// has only one element corresponding to the executed block associated to
/// the state.
pub fn executed_block_receipts_ref(&self) -> &[N::Receipt] {
let receipts = self.receipts();
debug_assert!(
receipts.len() <= 1,
"Expected at most one block's worth of receipts, found {}",
receipts.len()
);
receipts.first().map(|receipts| receipts.deref()).unwrap_or_default()
}
/// Returns a vector of __parent__ `BlockStates`.
///
/// The block state order in the output vector is newest to oldest (highest to lowest):
@@ -719,16 +743,17 @@ impl<N: NodePrimitives> BlockState<N> {
}
/// Represents an executed block stored in-memory.
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Clone, Debug)]
pub struct ExecutedBlock<N: NodePrimitives = EthPrimitives> {
/// Recovered Block
pub recovered_block: Arc<RecoveredBlock<N::Block>>,
/// Block's execution outcome.
pub execution_output: Arc<ExecutionOutcome<N::Receipt>>,
/// Block's hashed state.
pub hashed_state: Arc<HashedPostState>,
/// Trie updates that result from calculating the state root for the block.
pub trie_updates: Arc<TrieUpdates>,
/// Deferred trie data produced by execution.
///
/// This allows deferring the computation of the trie data which can be expensive.
/// The data can be populated asynchronously after the block was validated.
pub trie_data: DeferredTrieData,
}
impl<N: NodePrimitives> Default for ExecutedBlock<N> {
@@ -736,13 +761,54 @@ impl<N: NodePrimitives> Default for ExecutedBlock<N> {
Self {
recovered_block: Default::default(),
execution_output: Default::default(),
hashed_state: Default::default(),
trie_updates: Default::default(),
trie_data: DeferredTrieData::ready(ComputedTrieData::default()),
}
}
}
impl<N: NodePrimitives> PartialEq for ExecutedBlock<N> {
fn eq(&self, other: &Self) -> bool {
// Trie data is computed asynchronously and doesn't define block identity.
self.recovered_block == other.recovered_block &&
self.execution_output == other.execution_output
}
}
impl<N: NodePrimitives> ExecutedBlock<N> {
/// Create a new [`ExecutedBlock`] with already-computed trie data.
///
/// Use this constructor when trie data is available immediately (e.g., sequencers,
/// payload builders). This is the safe default path.
pub fn new(
recovered_block: Arc<RecoveredBlock<N::Block>>,
execution_output: Arc<ExecutionOutcome<N::Receipt>>,
trie_data: ComputedTrieData,
) -> Self {
Self { recovered_block, execution_output, trie_data: DeferredTrieData::ready(trie_data) }
}
/// Create a new [`ExecutedBlock`] with deferred trie data.
///
/// This is useful if the trie data is populated somewhere else, e.g. asynchronously
/// after the block was validated.
///
/// The [`DeferredTrieData`] handle allows expensive trie operations (sorting hashed state,
/// sorting trie updates, and building the accumulated trie input overlay) to be performed
/// outside the critical validation path. This can improve latency for time-sensitive
/// operations like block validation.
///
/// If the data hasn't been populated when [`Self::trie_data()`] is called, computation
/// occurs synchronously from stored inputs, so there is no blocking or deadlock risk.
///
/// Use [`Self::new()`] instead when trie data is already computed and available immediately.
pub const fn with_deferred_trie_data(
recovered_block: Arc<RecoveredBlock<N::Block>>,
execution_output: Arc<ExecutionOutcome<N::Receipt>>,
trie_data: DeferredTrieData,
) -> Self {
Self { recovered_block, execution_output, trie_data }
}
/// Returns a reference to an inner [`SealedBlock`]
#[inline]
pub fn sealed_block(&self) -> &SealedBlock<N::Block> {
@@ -761,16 +827,55 @@ impl<N: NodePrimitives> ExecutedBlock<N> {
&self.execution_output
}
/// Returns a reference to the hashed state result of the execution outcome
/// Returns the trie data, computing it synchronously if not already cached.
///
/// Uses `OnceLock::get_or_init` internally:
/// - If already computed: returns cached result immediately
/// - If not computed: first caller computes, others wait for that result
#[inline]
pub fn hashed_state(&self) -> &HashedPostState {
&self.hashed_state
#[tracing::instrument(level = "debug", target = "engine::tree", name = "trie_data", skip_all)]
pub fn trie_data(&self) -> ComputedTrieData {
self.trie_data.wait_cloned()
}
/// Returns a reference to the trie updates resulting from the execution outcome
/// Returns a clone of the deferred trie data handle.
///
/// A handle is a lightweight reference that can be passed to descendants without
/// forcing trie data to be computed immediately. The actual work runs when
/// `wait_cloned()` is called by a consumer (e.g. when merging overlays).
#[inline]
pub fn trie_updates(&self) -> &TrieUpdates {
&self.trie_updates
pub fn trie_data_handle(&self) -> DeferredTrieData {
self.trie_data.clone()
}
/// Returns the hashed state result of the execution outcome.
///
/// May compute trie data synchronously if the deferred task hasn't completed.
#[inline]
pub fn hashed_state(&self) -> Arc<HashedPostStateSorted> {
self.trie_data().hashed_state
}
/// Returns the trie updates resulting from the execution outcome.
///
/// May compute trie data synchronously if the deferred task hasn't completed.
#[inline]
pub fn trie_updates(&self) -> Arc<TrieUpdatesSorted> {
self.trie_data().trie_updates
}
/// Returns the trie input anchored to the persisted ancestor.
///
/// May compute trie data synchronously if the deferred task hasn't completed.
#[inline]
pub fn trie_input(&self) -> Option<Arc<TrieInputSorted>> {
self.trie_data().trie_input().cloned()
}
/// Returns the anchor hash of the trie input, if present.
#[inline]
pub fn anchor_hash(&self) -> Option<B256> {
self.trie_data().anchor_hash()
}
/// Returns a [`BlockNumber`] of the block.
@@ -875,8 +980,8 @@ mod tests {
StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider,
};
use reth_trie::{
AccountProof, HashedStorage, MultiProof, MultiProofTargets, StorageMultiProof,
StorageProof, TrieInput,
updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof,
MultiProofTargets, StorageMultiProof, StorageProof, TrieInput,
};
fn create_mock_state(

View File

@@ -11,6 +11,9 @@
mod in_memory;
pub use in_memory::*;
mod deferred_trie;
pub use deferred_trie::*;
mod noop;
mod chain_info;

View File

@@ -53,11 +53,10 @@ impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> {
/// Return lazy-loaded trie state aggregated from in-memory blocks.
fn trie_input(&self) -> &TrieInput {
self.trie_input.get_or_init(|| {
TrieInput::from_blocks(
self.in_memory
.iter()
.rev()
.map(|block| (block.hashed_state.as_ref(), block.trie_updates.as_ref())),
let bundles: Vec<_> =
self.in_memory.iter().rev().map(|block| block.trie_data()).collect();
TrieInput::from_blocks_sorted(
bundles.iter().map(|data| (data.hashed_state.as_ref(), data.trie_updates.as_ref())),
)
})
}

View File

@@ -1,6 +1,6 @@
use crate::{
in_memory::ExecutedBlock, CanonStateNotification, CanonStateNotifications,
CanonStateSubscriptions,
CanonStateSubscriptions, ComputedTrieData,
};
use alloy_consensus::{Header, SignableTransaction, TxEip1559, TxReceipt, EMPTY_ROOT_HASH};
use alloy_eips::{
@@ -23,7 +23,7 @@ use reth_primitives_traits::{
SignedTransaction,
};
use reth_storage_api::NodePrimitivesProvider;
use reth_trie::{root::state_root_unhashed, updates::TrieUpdates, HashedPostState};
use reth_trie::root::state_root_unhashed;
use revm_database::BundleState;
use revm_state::AccountInfo;
use std::{
@@ -92,7 +92,7 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
&mut self,
number: BlockNumber,
parent_hash: B256,
) -> RecoveredBlock<reth_ethereum_primitives::Block> {
) -> SealedBlock<reth_ethereum_primitives::Block> {
let mut rng = rand::rng();
let mock_tx = |nonce: u64| -> Recovered<_> {
@@ -167,17 +167,14 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
..Default::default()
};
let block = SealedBlock::from_sealed_parts(
SealedBlock::from_sealed_parts(
SealedHeader::seal_slow(header),
BlockBody {
transactions: transactions.into_iter().map(|tx| tx.into_inner()).collect(),
ommers: Vec::new(),
withdrawals: Some(vec![].into()),
},
);
RecoveredBlock::try_recover_sealed_with_senders(block, vec![self.signer; num_txs as usize])
.unwrap()
)
}
/// Creates a fork chain with the given base block.
@@ -191,7 +188,9 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
for _ in 0..length {
let block = self.generate_random_block(parent.number + 1, parent.hash());
parent = block.clone_sealed_block();
parent = block.clone();
let senders = vec![self.signer; block.body().transactions.len()];
let block = block.with_senders(senders);
fork.push(block);
}
@@ -205,20 +204,19 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
receipts: Vec<Vec<Receipt>>,
parent_hash: B256,
) -> ExecutedBlock {
let block_with_senders = self.generate_random_block(block_number, parent_hash);
let (block, senders) = block_with_senders.split_sealed();
ExecutedBlock {
recovered_block: Arc::new(RecoveredBlock::new_sealed(block, senders)),
execution_output: Arc::new(ExecutionOutcome::new(
let block = self.generate_random_block(block_number, parent_hash);
let senders = vec![self.signer; block.body().transactions.len()];
let trie_data = ComputedTrieData::default();
ExecutedBlock::new(
Arc::new(RecoveredBlock::new_sealed(block, senders)),
Arc::new(ExecutionOutcome::new(
BundleState::default(),
receipts,
block_number,
vec![Requests::default()],
)),
hashed_state: Arc::new(HashedPostState::default()),
trie_updates: Arc::new(TrieUpdates::default()),
}
trie_data,
)
}
/// Generates an [`ExecutedBlock`] that includes the given receipts.

View File

@@ -30,8 +30,9 @@ pub use info::ChainInfo;
#[cfg(any(test, feature = "test-utils"))]
pub use spec::test_fork_ids;
pub use spec::{
make_genesis_header, BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder,
ChainSpecProvider, DepositContract, ForkBaseFeeParams, DEV, HOLESKY, HOODI, MAINNET, SEPOLIA,
blob_params_to_schedule, create_chain_config, mainnet_chain_config, make_genesis_header,
BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder, ChainSpecProvider,
DepositContract, ForkBaseFeeParams, DEV, HOLESKY, HOODI, MAINNET, SEPOLIA,
};
use reth_primitives_traits::sync::OnceLock;

View File

@@ -10,7 +10,14 @@ use crate::{
sepolia::SEPOLIA_PARIS_BLOCK,
EthChainSpec,
};
use alloc::{boxed::Box, format, sync::Arc, vec::Vec};
use alloc::{
boxed::Box,
collections::BTreeMap,
format,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use alloy_chains::{Chain, NamedChain};
use alloy_consensus::{
constants::{
@@ -23,7 +30,7 @@ use alloy_eips::{
eip1559::INITIAL_BASE_FEE, eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams,
eip7892::BlobScheduleBlobParams,
};
use alloy_genesis::Genesis;
use alloy_genesis::{ChainConfig, Genesis};
use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
use alloy_trie::root::state_root_ref_unhashed;
use core::fmt::Debug;
@@ -73,6 +80,8 @@ pub fn make_genesis_header(genesis: &Genesis, hardforks: &ChainHardforks) -> Hea
.then_some(EMPTY_REQUESTS_HASH);
Header {
number: genesis.number.unwrap_or_default(),
parent_hash: genesis.parent_hash.unwrap_or_default(),
gas_limit: genesis.gas_limit,
difficulty: genesis.difficulty,
nonce: genesis.nonce.into(),
@@ -240,6 +249,111 @@ pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
.into()
});
/// Creates a [`ChainConfig`] from the given chain, hardforks, deposit contract address, and blob
/// schedule.
pub fn create_chain_config(
chain: Option<Chain>,
hardforks: &ChainHardforks,
deposit_contract_address: Option<Address>,
blob_schedule: BTreeMap<String, BlobParams>,
) -> ChainConfig {
// Helper to extract block number from a hardfork condition
let block_num = |fork: EthereumHardfork| hardforks.fork(fork).block_number();
// Helper to extract timestamp from a hardfork condition
let timestamp = |fork: EthereumHardfork| -> Option<u64> {
match hardforks.fork(fork) {
ForkCondition::Timestamp(t) => Some(t),
_ => None,
}
};
// Extract TTD from Paris fork
let (terminal_total_difficulty, terminal_total_difficulty_passed) =
match hardforks.fork(EthereumHardfork::Paris) {
ForkCondition::TTD { total_difficulty, .. } => (Some(total_difficulty), true),
_ => (None, false),
};
// Check if DAO fork is supported (it has an activation block)
let dao_fork_support = hardforks.fork(EthereumHardfork::Dao) != ForkCondition::Never;
ChainConfig {
chain_id: chain.map(|c| c.id()).unwrap_or(0),
homestead_block: block_num(EthereumHardfork::Homestead),
dao_fork_block: block_num(EthereumHardfork::Dao),
dao_fork_support,
eip150_block: block_num(EthereumHardfork::Tangerine),
eip155_block: block_num(EthereumHardfork::SpuriousDragon),
eip158_block: block_num(EthereumHardfork::SpuriousDragon),
byzantium_block: block_num(EthereumHardfork::Byzantium),
constantinople_block: block_num(EthereumHardfork::Constantinople),
petersburg_block: block_num(EthereumHardfork::Petersburg),
istanbul_block: block_num(EthereumHardfork::Istanbul),
muir_glacier_block: block_num(EthereumHardfork::MuirGlacier),
berlin_block: block_num(EthereumHardfork::Berlin),
london_block: block_num(EthereumHardfork::London),
arrow_glacier_block: block_num(EthereumHardfork::ArrowGlacier),
gray_glacier_block: block_num(EthereumHardfork::GrayGlacier),
merge_netsplit_block: None,
shanghai_time: timestamp(EthereumHardfork::Shanghai),
cancun_time: timestamp(EthereumHardfork::Cancun),
prague_time: timestamp(EthereumHardfork::Prague),
osaka_time: timestamp(EthereumHardfork::Osaka),
bpo1_time: timestamp(EthereumHardfork::Bpo1),
bpo2_time: timestamp(EthereumHardfork::Bpo2),
bpo3_time: timestamp(EthereumHardfork::Bpo3),
bpo4_time: timestamp(EthereumHardfork::Bpo4),
bpo5_time: timestamp(EthereumHardfork::Bpo5),
terminal_total_difficulty,
terminal_total_difficulty_passed,
ethash: None,
clique: None,
parlia: None,
extra_fields: Default::default(),
deposit_contract_address,
blob_schedule,
}
}
/// Returns a [`ChainConfig`] for the current Ethereum mainnet chain.
pub fn mainnet_chain_config() -> ChainConfig {
let hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
let blob_schedule = blob_params_to_schedule(&MAINNET.blob_params, &hardforks);
create_chain_config(
Some(Chain::mainnet()),
&hardforks,
Some(MAINNET_DEPOSIT_CONTRACT.address),
blob_schedule,
)
}
/// Converts the given [`BlobScheduleBlobParams`] into blobs schedule.
pub fn blob_params_to_schedule(
params: &BlobScheduleBlobParams,
hardforks: &ChainHardforks,
) -> BTreeMap<String, BlobParams> {
let mut schedule = BTreeMap::new();
schedule.insert("cancun".to_string(), params.cancun);
schedule.insert("prague".to_string(), params.prague);
schedule.insert("osaka".to_string(), params.osaka);
// Map scheduled entries back to bpo fork names by matching timestamps
let bpo_forks = EthereumHardfork::bpo_variants();
for (timestamp, blob_params) in &params.scheduled {
for bpo_fork in bpo_forks {
if let ForkCondition::Timestamp(fork_ts) = hardforks.fork(bpo_fork) &&
fork_ts == *timestamp
{
schedule.insert(bpo_fork.name().to_lowercase(), *blob_params);
break;
}
}
}
schedule
}
/// A wrapper around [`BaseFeeParams`] that allows for specifying constant or dynamic EIP-1559
/// parameters based on the active [Hardfork].
#[derive(Clone, Debug, PartialEq, Eq)]
@@ -514,8 +628,15 @@ impl<H: BlockHeader> ChainSpec<H> {
/// Compute the [`ForkId`] for the given [`Head`] following eip-6122 spec.
///
/// Note: In case there are multiple hardforks activated at the same block or timestamp, only
/// the first gets applied.
/// The fork hash is computed by starting from the genesis hash and iteratively adding
/// block numbers (for block-based forks) or timestamps (for timestamp-based forks) of
/// active forks. The `next` field indicates the next fork activation point, or `0` if
/// all forks are active.
///
/// Block-based forks are processed first, then timestamp-based forks. Multiple hardforks
/// activated at the same block or timestamp: only the first one is applied.
///
/// See: <https://eips.ethereum.org/EIPS/eip-6122>
pub fn fork_id(&self, head: &Head) -> ForkId {
let mut forkhash = ForkHash::from(self.genesis_hash());
@@ -572,6 +693,10 @@ impl<H: BlockHeader> ChainSpec<H> {
}
/// An internal helper function that returns a head block that satisfies a given Fork condition.
///
/// Creates a [`Head`] representation for a fork activation point, used by [`Self::fork_id`] to
/// compute fork IDs. For timestamp-based forks, includes the last block-based fork number
/// before the merge (if any).
pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head {
match cond {
ForkCondition::Block(number) => Head { number, ..Default::default() },
@@ -925,9 +1050,16 @@ impl ChainSpecBuilder {
self
}
/// Enable Dao at genesis.
pub fn dao_activated(mut self) -> Self {
self = self.frontier_activated();
self.hardforks.insert(EthereumHardfork::Dao, ForkCondition::Block(0));
self
}
/// Enable Homestead at genesis.
pub fn homestead_activated(mut self) -> Self {
self = self.frontier_activated();
self = self.dao_activated();
self.hardforks.insert(EthereumHardfork::Homestead, ForkCondition::Block(0));
self
}
@@ -974,9 +1106,16 @@ impl ChainSpecBuilder {
self
}
/// Enable Muir Glacier at genesis.
pub fn muirglacier_activated(mut self) -> Self {
self = self.istanbul_activated();
self.hardforks.insert(EthereumHardfork::MuirGlacier, ForkCondition::Block(0));
self
}
/// Enable Berlin at genesis.
pub fn berlin_activated(mut self) -> Self {
self = self.istanbul_activated();
self = self.muirglacier_activated();
self.hardforks.insert(EthereumHardfork::Berlin, ForkCondition::Block(0));
self
}
@@ -988,9 +1127,23 @@ impl ChainSpecBuilder {
self
}
/// Enable Arrow Glacier at genesis.
pub fn arrowglacier_activated(mut self) -> Self {
self = self.london_activated();
self.hardforks.insert(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0));
self
}
/// Enable Gray Glacier at genesis.
pub fn grayglacier_activated(mut self) -> Self {
self = self.arrowglacier_activated();
self.hardforks.insert(EthereumHardfork::GrayGlacier, ForkCondition::Block(0));
self
}
/// Enable Paris at genesis.
pub fn paris_activated(mut self) -> Self {
self = self.london_activated();
self = self.grayglacier_activated();
self.hardforks.insert(
EthereumHardfork::Paris,
ForkCondition::TTD {
@@ -1363,72 +1516,72 @@ Post-merge hard forks (timestamp based):
&[
(
EthereumHardfork::Frontier,
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
),
(
EthereumHardfork::Homestead,
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
),
(
EthereumHardfork::Dao,
ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
),
(
EthereumHardfork::Tangerine,
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
),
(
EthereumHardfork::SpuriousDragon,
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
),
(
EthereumHardfork::Byzantium,
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
),
(
EthereumHardfork::Constantinople,
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
),
(
EthereumHardfork::Petersburg,
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
),
(
EthereumHardfork::Istanbul,
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
),
(
EthereumHardfork::MuirGlacier,
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
),
(
EthereumHardfork::Berlin,
ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
),
(
EthereumHardfork::London,
ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
),
(
EthereumHardfork::ArrowGlacier,
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
),
(
EthereumHardfork::GrayGlacier,
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
),
(
EthereumHardfork::Shanghai,
ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
),
(
EthereumHardfork::Cancun,
ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
),
(
EthereumHardfork::Prague,
ForkId {
hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]),
hash: ForkHash(hex!("0xc376cf8b")),
next: mainnet::MAINNET_OSAKA_TIMESTAMP,
},
),
@@ -1443,60 +1596,60 @@ Post-merge hard forks (timestamp based):
&[
(
EthereumHardfork::Frontier,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::Homestead,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::Tangerine,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::SpuriousDragon,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::Byzantium,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::Constantinople,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::Petersburg,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::Istanbul,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::Berlin,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::London,
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
EthereumHardfork::Paris,
ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
),
(
EthereumHardfork::Shanghai,
ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
),
(
EthereumHardfork::Cancun,
ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
),
(
EthereumHardfork::Prague,
ForkId {
hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]),
hash: ForkHash(hex!("0xed88b5fd")),
next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
},
),
@@ -1511,71 +1664,71 @@ Post-merge hard forks (timestamp based):
&[
(
Head { number: 0, ..Default::default() },
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
),
(
Head { number: 1150000, ..Default::default() },
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
),
(
Head { number: 1920000, ..Default::default() },
ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
),
(
Head { number: 2463000, ..Default::default() },
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
),
(
Head { number: 2675000, ..Default::default() },
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
),
(
Head { number: 4370000, ..Default::default() },
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
),
(
Head { number: 7280000, ..Default::default() },
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
),
(
Head { number: 9069000, ..Default::default() },
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
),
(
Head { number: 9200000, ..Default::default() },
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
),
(
Head { number: 12244000, ..Default::default() },
ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
),
(
Head { number: 12965000, ..Default::default() },
ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
),
(
Head { number: 13773000, ..Default::default() },
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
),
(
Head { number: 15050000, ..Default::default() },
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
),
// First Shanghai block
(
Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
),
// First Cancun block
(
Head { number: 20000001, timestamp: 1710338135, ..Default::default() },
ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
),
// First Prague block
(
Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
ForkId {
hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]),
hash: ForkHash(hex!("0xc376cf8b")),
next: mainnet::MAINNET_OSAKA_TIMESTAMP,
},
),
@@ -1602,13 +1755,13 @@ Post-merge hard forks (timestamp based):
&[
(
Head { number: 0, ..Default::default() },
ForkId { hash: ForkHash([0xbe, 0xf7, 0x1d, 0x30]), next: 1742999832 },
ForkId { hash: ForkHash(hex!("0xbef71d30")), next: 1742999832 },
),
// First Prague block
(
Head { number: 0, timestamp: 1742999833, ..Default::default() },
ForkId {
hash: ForkHash([0x09, 0x29, 0xe2, 0x4e]),
hash: ForkHash(hex!("0x0929e24e")),
next: hoodi::HOODI_OSAKA_TIMESTAMP,
},
),
@@ -1635,43 +1788,43 @@ Post-merge hard forks (timestamp based):
&[
(
Head { number: 0, ..Default::default() },
ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
),
// First MergeNetsplit block
(
Head { number: 123, ..Default::default() },
ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
),
// Last MergeNetsplit block
(
Head { number: 123, timestamp: 1696000703, ..Default::default() },
ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
),
// First Shanghai block
(
Head { number: 123, timestamp: 1696000704, ..Default::default() },
ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
ForkId { hash: ForkHash(hex!("0xfd4f016b")), next: 1707305664 },
),
// Last Shanghai block
(
Head { number: 123, timestamp: 1707305663, ..Default::default() },
ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
ForkId { hash: ForkHash(hex!("0xfd4f016b")), next: 1707305664 },
),
// First Cancun block
(
Head { number: 123, timestamp: 1707305664, ..Default::default() },
ForkId { hash: ForkHash([0x9b, 0x19, 0x2a, 0xd0]), next: 1740434112 },
ForkId { hash: ForkHash(hex!("0x9b192ad0")), next: 1740434112 },
),
// Last Cancun block
(
Head { number: 123, timestamp: 1740434111, ..Default::default() },
ForkId { hash: ForkHash([0x9b, 0x19, 0x2a, 0xd0]), next: 1740434112 },
ForkId { hash: ForkHash(hex!("0x9b192ad0")), next: 1740434112 },
),
// First Prague block
(
Head { number: 123, timestamp: 1740434112, ..Default::default() },
ForkId {
hash: ForkHash([0xdf, 0xbd, 0x9b, 0xed]),
hash: ForkHash(hex!("0xdfbd9bed")),
next: holesky::HOLESKY_OSAKA_TIMESTAMP,
},
),
@@ -1698,45 +1851,45 @@ Post-merge hard forks (timestamp based):
&[
(
Head { number: 0, ..Default::default() },
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
Head { number: 1735370, ..Default::default() },
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
),
(
Head { number: 1735371, ..Default::default() },
ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
),
(
Head { number: 1735372, timestamp: 1677557087, ..Default::default() },
ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
),
// First Shanghai block
(
Head { number: 1735373, timestamp: 1677557088, ..Default::default() },
ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
),
// Last Shanghai block
(
Head { number: 1735374, timestamp: 1706655071, ..Default::default() },
ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
),
// First Cancun block
(
Head { number: 1735375, timestamp: 1706655072, ..Default::default() },
ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
),
// Last Cancun block
(
Head { number: 1735376, timestamp: 1741159775, ..Default::default() },
ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
),
// First Prague block
(
Head { number: 1735377, timestamp: 1741159776, ..Default::default() },
ForkId {
hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]),
hash: ForkHash(hex!("0xed88b5fd")),
next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
},
),
@@ -1762,7 +1915,7 @@ Post-merge hard forks (timestamp based):
&DEV,
&[(
Head { number: 0, ..Default::default() },
ForkId { hash: ForkHash([0x0b, 0x1a, 0x4e, 0xf7]), next: 0 },
ForkId { hash: ForkHash(hex!("0x0b1a4ef7")), next: 0 },
)],
)
}
@@ -1778,128 +1931,128 @@ Post-merge hard forks (timestamp based):
&[
(
Head { number: 0, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
), // Unsynced
(
Head { number: 1149999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
), // Last Frontier block
(
Head { number: 1150000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
), // First Homestead block
(
Head { number: 1919999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
), // Last Homestead block
(
Head { number: 1920000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
), // First DAO block
(
Head { number: 2462999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
), // Last DAO block
(
Head { number: 2463000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
), // First Tangerine block
(
Head { number: 2674999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
), // Last Tangerine block
(
Head { number: 2675000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
), // First Spurious block
(
Head { number: 4369999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
), // Last Spurious block
(
Head { number: 4370000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
), // First Byzantium block
(
Head { number: 7279999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
), // Last Byzantium block
(
Head { number: 7280000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
), // First and last Constantinople, first Petersburg block
(
Head { number: 9068999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
), // Last Petersburg block
(
Head { number: 9069000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
), // First Istanbul and first Muir Glacier block
(
Head { number: 9199999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
), // Last Istanbul and first Muir Glacier block
(
Head { number: 9200000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
), // First Muir Glacier block
(
Head { number: 12243999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
), // Last Muir Glacier block
(
Head { number: 12244000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
), // First Berlin block
(
Head { number: 12964999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
), // Last Berlin block
(
Head { number: 12965000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
), // First London block
(
Head { number: 13772999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
), // Last London block
(
Head { number: 13773000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
), // First Arrow Glacier block
(
Head { number: 15049999, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
), // Last Arrow Glacier block
(
Head { number: 15050000, timestamp: 0, ..Default::default() },
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
), // First Gray Glacier block
(
Head { number: 19999999, timestamp: 1667999999, ..Default::default() },
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
), // Last Gray Glacier block
(
Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
), // Last Shanghai block
(
Head { number: 20000001, timestamp: 1710338134, ..Default::default() },
ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
), // First Cancun block
(
Head { number: 20000002, timestamp: 1710338135, ..Default::default() },
ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
), // Last Cancun block
(
Head { number: 20000003, timestamp: 1746612310, ..Default::default() },
ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
), // First Prague block
(
Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
ForkId {
hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]),
hash: ForkHash(hex!("0xc376cf8b")),
next: mainnet::MAINNET_OSAKA_TIMESTAMP,
},
),
@@ -2369,7 +2522,7 @@ Post-merge hard forks (timestamp based):
let chainspec = ChainSpec::from(genesis);
// make sure we are at ForkHash("bc0c2605") with Head post-cancun
let expected_forkid = ForkId { hash: ForkHash([0xbc, 0x0c, 0x26, 0x05]), next: 0 };
let expected_forkid = ForkId { hash: ForkHash(hex!("0xbc0c2605")), next: 0 };
let got_forkid =
chainspec.fork_id(&Head { number: 73, timestamp: 840, ..Default::default() });
@@ -2479,7 +2632,7 @@ Post-merge hard forks (timestamp based):
assert_eq!(genesis_hash, expected_hash);
// check that the forkhash is correct
let expected_forkhash = ForkHash(hex!("8062457a"));
let expected_forkhash = ForkHash(hex!("0x8062457a"));
assert_eq!(ForkHash::from(genesis_hash), expected_forkhash);
}

View File

@@ -49,6 +49,7 @@ reth-stages.workspace = true
reth-stages-types = { workspace = true, optional = true }
reth-static-file-types = { workspace = true, features = ["clap"] }
reth-static-file.workspace = true
reth-tasks.workspace = true
reth-trie = { workspace = true, features = ["metrics"] }
reth-trie-db = { workspace = true, features = ["metrics"] }
reth-trie-common.workspace = true
@@ -82,6 +83,7 @@ backon.workspace = true
secp256k1 = { workspace = true, features = ["global-context", "std", "recovery"] }
tokio-stream.workspace = true
reqwest.workspace = true
metrics.workspace = true
# io
fdlimit.workspace = true

View File

@@ -1,5 +1,7 @@
//! Contains common `reth` arguments
pub use reth_primitives_traits::header::HeaderMut;
use alloy_primitives::B256;
use clap::Parser;
use reth_chainspec::EthChainSpec;
@@ -7,7 +9,7 @@ use reth_cli::chainspec::ChainSpecParser;
use reth_config::{config::EtlConfig, Config};
use reth_consensus::noop::NoopConsensus;
use reth_db::{init_db, open_db_read_only, DatabaseEnv};
use reth_db_common::init::init_genesis;
use reth_db_common::init::init_genesis_with_settings;
use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader};
use reth_eth_wire::NetPrimitivesFor;
use reth_evm::{noop::NoopEvmConfig, ConfigureEvm};
@@ -17,11 +19,14 @@ use reth_node_builder::{
Node, NodeComponents, NodeComponentsBuilder, NodeTypes, NodeTypesWithDBAdapter,
};
use reth_node_core::{
args::{DatabaseArgs, DatadirArgs},
args::{DatabaseArgs, DatadirArgs, StaticFilesArgs},
dirs::{ChainPath, DataDirPath},
};
use reth_provider::{
providers::{BlockchainProvider, NodeTypesForProvider, StaticFileProvider},
providers::{
BlockchainProvider, NodeTypesForProvider, RocksDBProvider, StaticFileProvider,
StaticFileProviderBuilder,
},
ProviderFactory, StaticFileProviderFactory,
};
use reth_stages::{sets::DefaultStages, Pipeline, PipelineTarget};
@@ -57,6 +62,10 @@ pub struct EnvironmentArgs<C: ChainSpecParser> {
/// All database related arguments
#[command(flatten)]
pub db: DatabaseArgs,
/// All static files related arguments
#[command(flatten)]
pub static_files: StaticFilesArgs,
}
impl<C: ChainSpecParser> EnvironmentArgs<C> {
@@ -69,10 +78,12 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
let data_dir = self.datadir.clone().resolve_datadir(self.chain.chain());
let db_path = data_dir.db();
let sf_path = data_dir.static_files();
let rocksdb_path = data_dir.rocksdb();
if access.is_read_write() {
reth_fs_util::create_dir_all(&db_path)?;
reth_fs_util::create_dir_all(&sf_path)?;
reth_fs_util::create_dir_all(&rocksdb_path)?;
}
let config_path = self.config.clone().unwrap_or_else(|| data_dir.config());
@@ -92,21 +103,35 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
}
info!(target: "reth::cli", ?db_path, ?sf_path, "Opening storage");
let genesis_block_number = self.chain.genesis().number.unwrap_or_default();
let (db, sfp) = match access {
AccessRights::RW => (
Arc::new(init_db(db_path, self.db.database_args())?),
StaticFileProvider::read_write(sf_path)?,
),
AccessRights::RO => (
Arc::new(open_db_read_only(&db_path, self.db.database_args())?),
StaticFileProvider::read_only(sf_path, false)?,
StaticFileProviderBuilder::read_write(sf_path)?
.with_genesis_block_number(genesis_block_number)
.build()?,
),
AccessRights::RO | AccessRights::RoInconsistent => {
(Arc::new(open_db_read_only(&db_path, self.db.database_args())?), {
let provider = StaticFileProviderBuilder::read_only(sf_path)?
.with_genesis_block_number(genesis_block_number)
.build()?;
provider.watch_directory();
provider
})
}
};
// TransactionDB only support read-write mode
let rocksdb_provider = RocksDBProvider::builder(data_dir.rocksdb())
.with_default_tables()
.with_database_log_level(self.db.log_level)
.build()?;
let provider_factory = self.create_provider_factory(&config, db, sfp)?;
let provider_factory =
self.create_provider_factory(&config, db, sfp, rocksdb_provider, access)?;
if access.is_read_write() {
debug!(target: "reth::cli", chain=%self.chain.chain(), genesis=?self.chain.genesis_hash(), "Initializing genesis");
init_genesis(&provider_factory)?;
init_genesis_with_settings(&provider_factory, self.static_files.to_settings())?;
}
Ok(Environment { config, provider_factory, data_dir })
@@ -122,23 +147,25 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
config: &Config,
db: Arc<DatabaseEnv>,
static_file_provider: StaticFileProvider<N::Primitives>,
rocksdb_provider: RocksDBProvider,
access: AccessRights,
) -> eyre::Result<ProviderFactory<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>>
where
C: ChainSpecParser<ChainSpec = N::ChainSpec>,
{
let has_receipt_pruning = config.prune.has_receipts_pruning();
let prune_modes = config.prune.segments.clone();
let factory = ProviderFactory::<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>::new(
db,
self.chain.clone(),
static_file_provider,
)
rocksdb_provider,
)?
.with_prune_modes(prune_modes.clone());
// Check for consistency between database and static files.
if let Some(unwind_target) = factory
.static_file_provider()
.check_consistency(&factory.provider()?, has_receipt_pruning)?
if !access.is_read_only_inconsistent() &&
let Some(unwind_target) =
factory.static_file_provider().check_consistency(&factory.provider()?)?
{
if factory.db_ref().is_read_only()? {
warn!(target: "reth::cli", ?unwind_target, "Inconsistent storage. Restart node to heal.");
@@ -199,6 +226,8 @@ pub enum AccessRights {
RW,
/// Read-only access
RO,
/// Read-only access with possibly inconsistent data
RoInconsistent,
}
impl AccessRights {
@@ -206,6 +235,12 @@ impl AccessRights {
pub const fn is_read_write(&self) -> bool {
matches!(self, Self::RW)
}
/// Returns `true` if it requires read-only access to the environment with possibly inconsistent
/// data.
pub const fn is_read_only_inconsistent(&self) -> bool {
matches!(self, Self::RoInconsistent)
}
}
/// Helper alias to satisfy `FullNodeTypes` bound on [`Node`] trait generic.
@@ -215,17 +250,6 @@ type FullTypesAdapter<T> = FullNodeTypesAdapter<
BlockchainProvider<NodeTypesWithDBAdapter<T, Arc<DatabaseEnv>>>,
>;
/// Trait for block headers that can be modified through CLI operations.
pub trait CliHeader {
fn set_number(&mut self, number: u64);
}
impl CliHeader for alloy_consensus::Header {
fn set_number(&mut self, number: u64) {
self.number = number;
}
}
/// Helper trait with a common set of requirements for the
/// [`NodeTypes`] in CLI.
pub trait CliNodeTypes: Node<FullTypesAdapter<Self>> + NodeTypesForProvider {

View File

@@ -22,13 +22,14 @@ impl Command {
let config = if self.default {
Config::default()
} else {
let path = self.config.clone().unwrap_or_default();
// Check if the file exists
let path = match self.config.as_ref() {
Some(path) => path,
None => bail!("No config file provided. Use --config <FILE> or pass --default"),
};
if !path.exists() {
bail!("Config file does not exist: {}", path.display());
}
// Read the configuration file
Config::from_path(&path)
Config::from_path(path)
.wrap_err_with(|| format!("Could not load config file: {}", path.display()))?
};
println!("{}", toml::to_string_pretty(&config)?);

View File

@@ -0,0 +1,92 @@
use alloy_primitives::{keccak256, Address};
use clap::Parser;
use human_bytes::human_bytes;
use reth_codecs::Compact;
use reth_db_api::{cursor::DbDupCursorRO, database::Database, tables, transaction::DbTx};
use reth_db_common::DbTool;
use reth_node_builder::NodeTypesWithDB;
use std::time::{Duration, Instant};
use tracing::info;
/// Log progress every 5 seconds
const LOG_INTERVAL: Duration = Duration::from_secs(5);
/// The arguments for the `reth db account-storage` command
#[derive(Parser, Debug)]
pub struct Command {
/// The account address to check storage for
address: Address,
}
impl Command {
/// Execute `db account-storage` command
pub fn execute<N: NodeTypesWithDB>(self, tool: &DbTool<N>) -> eyre::Result<()> {
let address = self.address;
let (slot_count, plain_size) = tool.provider_factory.db_ref().view(|tx| {
let mut cursor = tx.cursor_dup_read::<tables::PlainStorageState>()?;
let mut count = 0usize;
let mut total_value_bytes = 0usize;
let mut last_log = Instant::now();
// Walk all storage entries for this address
let walker = cursor.walk_dup(Some(address), None)?;
for entry in walker {
let (_, storage_entry) = entry?;
count += 1;
// StorageEntry encodes as: 32 bytes (key/subkey uncompressed) + compressed U256
let mut buf = Vec::new();
let entry_len = storage_entry.to_compact(&mut buf);
total_value_bytes += entry_len;
if last_log.elapsed() >= LOG_INTERVAL {
info!(
target: "reth::cli",
address = %address,
slots = count,
key = %storage_entry.key,
"Processing storage slots"
);
last_log = Instant::now();
}
}
// Add 20 bytes for the Address key (stored once per account in dupsort)
let total_size = if count > 0 { 20 + total_value_bytes } else { 0 };
Ok::<_, eyre::Report>((count, total_size))
})??;
// Estimate hashed storage size: 32-byte B256 key instead of 20-byte Address
let hashed_size_estimate = if slot_count > 0 { plain_size + 12 } else { 0 };
let total_estimate = plain_size + hashed_size_estimate;
let hashed_address = keccak256(address);
println!("Account: {address}");
println!("Hashed address: {hashed_address}");
println!("Storage slots: {slot_count}");
println!("Plain storage size: {} (estimated)", human_bytes(plain_size as f64));
println!("Hashed storage size: {} (estimated)", human_bytes(hashed_size_estimate as f64));
println!("Total estimated size: {}", human_bytes(total_estimate as f64));
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_address_arg() {
let cmd = Command::try_parse_from([
"account-storage",
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
])
.unwrap();
assert_eq!(
cmd.address,
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse::<Address>().unwrap()
);
}
}

View File

@@ -6,8 +6,9 @@ use reth_db_api::{
transaction::{DbTx, DbTxMut},
TableViewer, Tables,
};
use reth_db_common::DbTool;
use reth_node_builder::NodeTypesWithDB;
use reth_provider::{ProviderFactory, StaticFileProviderFactory};
use reth_provider::StaticFileProviderFactory;
use reth_static_file_types::StaticFileSegment;
/// The arguments for the `reth db clear` command
@@ -19,16 +20,13 @@ pub struct Command {
impl Command {
/// Execute `db clear` command
pub fn execute<N: NodeTypesWithDB>(
self,
provider_factory: ProviderFactory<N>,
) -> eyre::Result<()> {
pub fn execute<N: NodeTypesWithDB>(self, tool: &DbTool<N>) -> eyre::Result<()> {
match self.subcommand {
Subcommands::Mdbx { table } => {
table.view(&ClearViewer { db: provider_factory.db_ref() })?
table.view(&ClearViewer { db: tool.provider_factory.db_ref() })?
}
Subcommands::StaticFile { segment } => {
let static_file_provider = provider_factory.static_file_provider();
let static_file_provider = tool.provider_factory.static_file_provider();
let static_files = iter_static_files(static_file_provider.directory())?;
if let Some(segment_static_files) = static_files.get(&segment) {

View File

@@ -3,16 +3,22 @@ use clap::Parser;
use reth_db::{
static_file::{
ColumnSelectorOne, ColumnSelectorTwo, HeaderWithHashMask, ReceiptMask, TransactionMask,
TransactionSenderMask,
},
RawDupSort,
};
use reth_db_api::{
table::{Decompress, DupSort, Table},
tables, RawKey, RawTable, Receipts, TableViewer, Transactions,
cursor::{DbCursorRO, DbDupCursorRO},
database::Database,
table::{Compress, Decompress, DupSort, Table},
tables,
transaction::DbTx,
RawKey, RawTable, Receipts, TableViewer, Transactions,
};
use reth_db_common::DbTool;
use reth_node_api::{HeaderTy, ReceiptTy, TxTy};
use reth_node_builder::NodeTypesWithDB;
use reth_primitives_traits::ValueWithSubKey;
use reth_provider::{providers::ProviderNodeTypes, StaticFileProviderFactory};
use reth_static_file_types::StaticFileSegment;
use tracing::error;
@@ -38,6 +44,14 @@ enum Subcommand {
#[arg(value_parser = maybe_json_value_parser)]
subkey: Option<String>,
/// Optional end key for range query (exclusive upper bound)
#[arg(value_parser = maybe_json_value_parser)]
end_key: Option<String>,
/// Optional end subkey for range query (exclusive upper bound)
#[arg(value_parser = maybe_json_value_parser)]
end_subkey: Option<String>,
/// Output bytes instead of human-readable decoded value
#[arg(long)]
raw: bool,
@@ -60,8 +74,8 @@ impl Command {
/// Execute `db get` command
pub fn execute<N: ProviderNodeTypes>(self, tool: &DbTool<N>) -> eyre::Result<()> {
match self.subcommand {
Subcommand::Mdbx { table, key, subkey, raw } => {
table.view(&GetValueViewer { tool, key, subkey, raw })?
Subcommand::Mdbx { table, key, subkey, end_key, end_subkey, raw } => {
table.view(&GetValueViewer { tool, key, subkey, end_key, end_subkey, raw })?
}
Subcommand::StaticFile { segment, key, raw } => {
let (key, mask): (u64, _) = match segment {
@@ -75,19 +89,21 @@ impl Command {
StaticFileSegment::Receipts => {
(table_key::<tables::Receipts>(&key)?, <ReceiptMask<ReceiptTy<N>>>::MASK)
}
StaticFileSegment::TransactionSenders => (
table_key::<tables::TransactionSenders>(&key)?,
<TransactionSenderMask>::MASK,
),
};
let content = tool.provider_factory.static_file_provider().find_static_file(
segment,
|provider| {
let mut cursor = provider.cursor()?;
cursor.get(key.into(), mask).map(|result| {
result.map(|vec| {
vec.iter().map(|slice| slice.to_vec()).collect::<Vec<_>>()
})
})
},
)?;
let content = tool
.provider_factory
.static_file_provider()
.get_segment_provider(segment, key)?
.cursor()?
.get(key.into(), mask)
.map(|result| {
result.map(|vec| vec.iter().map(|slice| slice.to_vec()).collect::<Vec<_>>())
})?;
match content {
Some(content) => {
@@ -116,6 +132,13 @@ impl Command {
)?;
println!("{}", serde_json::to_string_pretty(&receipt)?);
}
StaticFileSegment::TransactionSenders => {
let sender =
<<tables::TransactionSenders as Table>::Value>::decompress(
content[0].as_slice(),
)?;
println!("{}", serde_json::to_string_pretty(&sender)?);
}
}
}
}
@@ -144,6 +167,8 @@ struct GetValueViewer<'a, N: NodeTypesWithDB> {
tool: &'a DbTool<N>,
key: String,
subkey: Option<String>,
end_key: Option<String>,
end_subkey: Option<String>,
raw: bool,
}
@@ -153,53 +178,158 @@ impl<N: ProviderNodeTypes> TableViewer<()> for GetValueViewer<'_, N> {
fn view<T: Table>(&self) -> Result<(), Self::Error> {
let key = table_key::<T>(&self.key)?;
let content = if self.raw {
self.tool
.get::<RawTable<T>>(RawKey::from(key))?
.map(|content| hex::encode_prefixed(content.raw_value()))
} else {
self.tool.get::<T>(key)?.as_ref().map(serde_json::to_string_pretty).transpose()?
};
// A non-dupsort table cannot have subkeys. The `subkey` arg becomes the `end_key`. First we
// check that `end_key` and `end_subkey` weren't previously given, as that wouldn't be
// valid.
if self.end_key.is_some() || self.end_subkey.is_some() {
return Err(eyre::eyre!("Only END_KEY can be given for non-DUPSORT tables"));
}
match content {
Some(content) => {
println!("{content}");
}
None => {
error!(target: "reth::cli", "No content for the given table key.");
}
};
let end_key = self.subkey.clone();
// Check if we're doing a range query
if let Some(ref end_key_str) = end_key {
let end_key = table_key::<T>(end_key_str)?;
// Use walk_range to iterate over the range
self.tool.provider_factory.db_ref().view(|tx| {
let mut cursor = tx.cursor_read::<T>()?;
let walker = cursor.walk_range(key..end_key)?;
for result in walker {
let (k, v) = result?;
let json_val = if self.raw {
let raw_key = RawKey::from(k);
serde_json::json!({
"key": hex::encode_prefixed(raw_key.raw_key()),
"val": hex::encode_prefixed(v.compress().as_ref()),
})
} else {
serde_json::json!({
"key": &k,
"val": &v,
})
};
println!("{}", serde_json::to_string_pretty(&json_val)?);
}
Ok::<_, eyre::Report>(())
})??;
} else {
// Single key lookup
let content = if self.raw {
self.tool
.get::<RawTable<T>>(RawKey::from(key))?
.map(|content| hex::encode_prefixed(content.raw_value()))
} else {
self.tool.get::<T>(key)?.as_ref().map(serde_json::to_string_pretty).transpose()?
};
match content {
Some(content) => {
println!("{content}");
}
None => {
error!(target: "reth::cli", "No content for the given table key.");
}
};
}
Ok(())
}
fn view_dupsort<T: DupSort>(&self) -> Result<(), Self::Error> {
fn view_dupsort<T: DupSort>(&self) -> Result<(), Self::Error>
where
T::Value: reth_primitives_traits::ValueWithSubKey<SubKey = T::SubKey>,
{
// get a key for given table
let key = table_key::<T>(&self.key)?;
// process dupsort table
let subkey = table_subkey::<T>(self.subkey.as_deref())?;
let content = if self.raw {
self.tool
.get_dup::<RawDupSort<T>>(RawKey::from(key), RawKey::from(subkey))?
.map(|content| hex::encode_prefixed(content.raw_value()))
} else {
self.tool
.get_dup::<T>(key, subkey)?
// Check if we're doing a range query
if let Some(ref end_key_str) = self.end_key {
let end_key = table_key::<T>(end_key_str)?;
let start_subkey = table_subkey::<T>(Some(
self.subkey.as_ref().expect("must have been given if end_key is given").as_str(),
))?;
let end_subkey_parsed = self
.end_subkey
.as_ref()
.map(serde_json::to_string_pretty)
.transpose()?
};
.map(|s| table_subkey::<T>(Some(s.as_str())))
.transpose()?;
match content {
Some(content) => {
println!("{content}");
}
None => {
error!(target: "reth::cli", "No content for the given table subkey.");
}
};
self.tool.provider_factory.db_ref().view(|tx| {
let mut cursor = tx.cursor_dup_read::<T>()?;
// Seek to the starting key. If there is actually a key at the starting key then
// seek to the subkey within it.
if let Some((decoded_key, _)) = cursor.seek(key.clone())? &&
decoded_key == key
{
cursor.seek_by_key_subkey(key.clone(), start_subkey.clone())?;
}
// Get the current position to start iteration
let mut current = cursor.current()?;
while let Some((decoded_key, decoded_value)) = current {
// Extract the subkey using the ValueWithSubKey trait
let decoded_subkey = decoded_value.get_subkey();
// Check if we've reached the end (exclusive)
if (&decoded_key, Some(&decoded_subkey)) >=
(&end_key, end_subkey_parsed.as_ref())
{
break;
}
// Output the entry with both key and subkey
let json_val = if self.raw {
let raw_key = RawKey::from(decoded_key.clone());
serde_json::json!({
"key": hex::encode_prefixed(raw_key.raw_key()),
"val": hex::encode_prefixed(decoded_value.compress().as_ref()),
})
} else {
serde_json::json!({
"key": &decoded_key,
"val": &decoded_value,
})
};
println!("{}", serde_json::to_string_pretty(&json_val)?);
// Move to next entry
current = cursor.next()?;
}
Ok::<_, eyre::Report>(())
})??;
} else {
// Single key/subkey lookup
let subkey = table_subkey::<T>(self.subkey.as_deref())?;
let content = if self.raw {
self.tool
.get_dup::<RawDupSort<T>>(RawKey::from(key), RawKey::from(subkey))?
.map(|content| hex::encode_prefixed(content.raw_value()))
} else {
self.tool
.get_dup::<T>(key, subkey)?
.as_ref()
.map(serde_json::to_string_pretty)
.transpose()?
};
match content {
Some(content) => {
println!("{content}");
}
None => {
error!(target: "reth::cli", "No content for the given table subkey.");
}
};
}
Ok(())
}
}

View File

@@ -3,7 +3,7 @@ use alloy_primitives::hex;
use clap::Parser;
use eyre::WrapErr;
use reth_chainspec::EthereumHardforks;
use reth_db::DatabaseEnv;
use reth_db::{transaction::DbTx, DatabaseEnv};
use reth_db_api::{database::Database, table::Table, RawValue, TableViewer, Tables};
use reth_db_common::{DbTool, ListFilter};
use reth_node_builder::{NodeTypes, NodeTypesWithDBAdapter};
@@ -96,6 +96,9 @@ impl<N: NodeTypes> TableViewer<()> for ListTableViewer<'_, N> {
fn view<T: Table>(&self) -> Result<(), Self::Error> {
self.tool.provider_factory.db_ref().view(|tx| {
// We may be using the tui for a long time
tx.disable_long_read_transaction_safety();
let table_db = tx.inner.open_db(Some(self.args.table.name())).wrap_err("Could not open db.")?;
let stats = tx.inner.db_stat(&table_db).wrap_err(format!("Could not find table: {}", self.args.table.name()))?;
let total_entries = stats.entries();

View File

@@ -2,18 +2,22 @@ use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
use clap::{Parser, Subcommand};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_runner::CliContext;
use reth_db::version::{get_db_version, DatabaseVersionError, DB_VERSION};
use reth_db_common::DbTool;
use std::{
io::{self, Write},
sync::Arc,
};
mod account_storage;
mod checksum;
mod clear;
mod diff;
mod get;
mod list;
mod repair_trie;
mod settings;
mod static_file_header;
mod stats;
/// DB List TUI
mod tui;
@@ -51,16 +55,23 @@ pub enum Subcommands {
Clear(clear::Command),
/// Verifies trie consistency and outputs any inconsistencies
RepairTrie(repair_trie::Command),
/// Reads and displays the static file segment header
StaticFileHeader(static_file_header::Command),
/// Lists current and local database versions
Version,
/// Returns the full database path
Path,
/// Manage storage settings
Settings(settings::Command),
/// Gets storage size information for an account
AccountStorage(account_storage::Command),
}
/// `db_ro_exec` opens a database in read-only mode, and then execute with the provided command
macro_rules! db_ro_exec {
($env:expr, $tool:ident, $N:ident, $command:block) => {
let Environment { provider_factory, .. } = $env.init::<$N>(AccessRights::RO)?;
/// Initializes a provider factory with specified access rights, and then execute with the provided
/// command
macro_rules! db_exec {
($env:expr, $tool:ident, $N:ident, $access_rights:expr, $command:block) => {
let Environment { provider_factory, .. } = $env.init::<$N>($access_rights)?;
let $tool = DbTool::new(provider_factory)?;
$command;
@@ -69,7 +80,10 @@ macro_rules! db_ro_exec {
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
/// Execute `db` command
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(self) -> eyre::Result<()> {
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(
self,
ctx: CliContext,
) -> eyre::Result<()> {
let data_dir = self.env.datadir.clone().resolve_datadir(self.env.chain.chain());
let db_path = data_dir.db();
let static_files_path = data_dir.static_files();
@@ -88,27 +102,32 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C>
match self.command {
// TODO: We'll need to add this on the DB trait.
Subcommands::Stats(command) => {
db_ro_exec!(self.env, tool, N, {
let access_rights = if command.skip_consistency_checks {
AccessRights::RoInconsistent
} else {
AccessRights::RO
};
db_exec!(self.env, tool, N, access_rights, {
command.execute(data_dir, &tool)?;
});
}
Subcommands::List(command) => {
db_ro_exec!(self.env, tool, N, {
db_exec!(self.env, tool, N, AccessRights::RO, {
command.execute(&tool)?;
});
}
Subcommands::Checksum(command) => {
db_ro_exec!(self.env, tool, N, {
db_exec!(self.env, tool, N, AccessRights::RO, {
command.execute(&tool)?;
});
}
Subcommands::Diff(command) => {
db_ro_exec!(self.env, tool, N, {
db_exec!(self.env, tool, N, AccessRights::RO, {
command.execute(&tool)?;
});
}
Subcommands::Get(command) => {
db_ro_exec!(self.env, tool, N, {
db_exec!(self.env, tool, N, AccessRights::RO, {
command.execute(&tool)?;
});
}
@@ -130,19 +149,26 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C>
}
}
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
let tool = DbTool::new(provider_factory)?;
tool.drop(db_path, static_files_path, exex_wal_path)?;
db_exec!(self.env, tool, N, AccessRights::RW, {
tool.drop(db_path, static_files_path, exex_wal_path)?;
});
}
Subcommands::Clear(command) => {
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
command.execute(provider_factory)?;
db_exec!(self.env, tool, N, AccessRights::RW, {
command.execute(&tool)?;
});
}
Subcommands::RepairTrie(command) => {
let access_rights =
if command.dry_run { AccessRights::RO } else { AccessRights::RW };
let Environment { provider_factory, .. } = self.env.init::<N>(access_rights)?;
command.execute(provider_factory)?;
db_exec!(self.env, tool, N, access_rights, {
command.execute(&tool, ctx.task_executor.clone())?;
});
}
Subcommands::StaticFileHeader(command) => {
db_exec!(self.env, tool, N, AccessRights::RoInconsistent, {
command.execute(&tool)?;
});
}
Subcommands::Version => {
let local_db_version = match get_db_version(&db_path) {
@@ -162,6 +188,16 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C>
Subcommands::Path => {
println!("{}", db_path.display());
}
Subcommands::Settings(command) => {
db_exec!(self.env, tool, N, command.access_rights(), {
command.execute(&tool)?;
});
}
Subcommands::AccountStorage(command) => {
db_exec!(self.env, tool, N, AccessRights::RO, {
command.execute(&tool)?;
});
}
}
Ok(())

View File

@@ -1,20 +1,34 @@
use clap::Parser;
use metrics::{self, Counter};
use reth_chainspec::EthChainSpec;
use reth_cli_util::parse_socket_address;
use reth_db_api::{
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO},
database::Database,
tables,
transaction::{DbTx, DbTxMut},
};
use reth_node_builder::NodeTypesWithDB;
use reth_provider::{providers::ProviderNodeTypes, ProviderFactory, StageCheckpointReader};
use reth_db_common::DbTool;
use reth_node_core::version::version_metadata;
use reth_node_metrics::{
chain::ChainSpecInfo,
hooks::Hooks,
server::{MetricServer, MetricServerConfig},
version::VersionInfo,
};
use reth_provider::{providers::ProviderNodeTypes, ChainSpecProvider, StageCheckpointReader};
use reth_stages::StageId;
use reth_tasks::TaskExecutor;
use reth_trie::{
verify::{Output, Verifier},
Nibbles,
};
use reth_trie_common::{StorageTrieEntry, StoredNibbles, StoredNibblesSubKey};
use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
use std::time::{Duration, Instant};
use std::{
net::SocketAddr,
time::{Duration, Instant},
};
use tracing::{info, warn};
const PROGRESS_PERIOD: Duration = Duration::from_secs(5);
@@ -25,27 +39,74 @@ pub struct Command {
/// Only show inconsistencies without making any repairs
#[arg(long)]
pub(crate) dry_run: bool,
/// Enable Prometheus metrics.
///
/// The metrics will be served at the given interface and port.
#[arg(long = "metrics", value_name = "ADDR:PORT", value_parser = parse_socket_address)]
pub(crate) metrics: Option<SocketAddr>,
}
impl Command {
/// Execute `db repair-trie` command
pub fn execute<N: ProviderNodeTypes>(
self,
provider_factory: ProviderFactory<N>,
tool: &DbTool<N>,
task_executor: TaskExecutor,
) -> eyre::Result<()> {
if self.dry_run {
verify_only(provider_factory)?
// Set up metrics server if requested
let _metrics_handle = if let Some(listen_addr) = self.metrics {
let chain_name = tool.provider_factory.chain_spec().chain().to_string();
let executor = task_executor.clone();
let handle = task_executor.spawn_critical("metrics server", async move {
let config = MetricServerConfig::new(
listen_addr,
VersionInfo {
version: version_metadata().cargo_pkg_version.as_ref(),
build_timestamp: version_metadata().vergen_build_timestamp.as_ref(),
cargo_features: version_metadata().vergen_cargo_features.as_ref(),
git_sha: version_metadata().vergen_git_sha.as_ref(),
target_triple: version_metadata().vergen_cargo_target_triple.as_ref(),
build_profile: version_metadata().build_profile_name.as_ref(),
},
ChainSpecInfo { name: chain_name },
executor,
Hooks::builder().build(),
);
// Spawn the metrics server
if let Err(e) = MetricServer::new(config).serve().await {
tracing::error!("Metrics server error: {}", e);
}
});
Some(handle)
} else {
verify_and_repair(provider_factory)?
None
};
if self.dry_run {
verify_only(tool)?
} else {
verify_and_repair(tool)?
}
Ok(())
}
}
fn verify_only<N: NodeTypesWithDB>(provider_factory: ProviderFactory<N>) -> eyre::Result<()> {
fn verify_only<N: ProviderNodeTypes>(tool: &DbTool<N>) -> eyre::Result<()> {
// Log the database block tip from Finish stage checkpoint
let finish_checkpoint = tool
.provider_factory
.provider()?
.get_stage_checkpoint(StageId::Finish)?
.unwrap_or_default();
info!("Database block tip: {}", finish_checkpoint.block_number);
// Get a database transaction directly from the database
let db = provider_factory.db_ref();
let db = tool.provider_factory.db_ref();
let mut tx = db.tx()?;
tx.disable_long_read_transaction_safety();
@@ -54,6 +115,8 @@ fn verify_only<N: NodeTypesWithDB>(provider_factory: ProviderFactory<N>) -> eyre
let trie_cursor_factory = DatabaseTrieCursorFactory::new(&tx);
let verifier = Verifier::new(&trie_cursor_factory, hashed_cursor_factory)?;
let metrics = RepairTrieMetrics::new();
let mut inconsistent_nodes = 0;
let start_time = Instant::now();
let mut last_progress_time = Instant::now();
@@ -70,6 +133,21 @@ fn verify_only<N: NodeTypesWithDB>(provider_factory: ProviderFactory<N>) -> eyre
} else {
warn!("Inconsistency found: {output:?}");
inconsistent_nodes += 1;
// Record metrics based on output type
match output {
Output::AccountExtra(_, _) |
Output::AccountWrong { .. } |
Output::AccountMissing(_, _) => {
metrics.account_inconsistencies.increment(1);
}
Output::StorageExtra(_, _, _) |
Output::StorageWrong { .. } |
Output::StorageMissing(_, _, _) => {
metrics.storage_inconsistencies.increment(1);
}
Output::Progress(_) => unreachable!(),
}
}
}
@@ -114,11 +192,13 @@ fn verify_checkpoints(provider: impl StageCheckpointReader) -> eyre::Result<()>
Ok(())
}
fn verify_and_repair<N: ProviderNodeTypes>(
provider_factory: ProviderFactory<N>,
) -> eyre::Result<()> {
fn verify_and_repair<N: ProviderNodeTypes>(tool: &DbTool<N>) -> eyre::Result<()> {
// Get a read-write database provider
let mut provider_rw = provider_factory.provider_rw()?;
let mut provider_rw = tool.provider_factory.provider_rw()?;
// Log the database block tip from Finish stage checkpoint
let finish_checkpoint = provider_rw.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default();
info!("Database block tip: {}", finish_checkpoint.block_number);
// Check that a pipeline sync isn't in progress.
verify_checkpoints(provider_rw.as_ref())?;
@@ -138,6 +218,8 @@ fn verify_and_repair<N: ProviderNodeTypes>(
// Create the verifier
let verifier = Verifier::new(&trie_cursor_factory, hashed_cursor_factory)?;
let metrics = RepairTrieMetrics::new();
let mut inconsistent_nodes = 0;
let start_time = Instant::now();
let mut last_progress_time = Instant::now();
@@ -149,6 +231,21 @@ fn verify_and_repair<N: ProviderNodeTypes>(
if !matches!(output, Output::Progress(_)) {
warn!("Inconsistency found, will repair: {output:?}");
inconsistent_nodes += 1;
// Record metrics based on output type
match &output {
Output::AccountExtra(_, _) |
Output::AccountWrong { .. } |
Output::AccountMissing(_, _) => {
metrics.account_inconsistencies.increment(1);
}
Output::StorageExtra(_, _, _) |
Output::StorageWrong { .. } |
Output::StorageMissing(_, _, _) => {
metrics.storage_inconsistencies.increment(1);
}
Output::Progress(_) => {}
}
}
match output {
@@ -247,3 +344,25 @@ fn output_progress(last_account: Nibbles, start_time: Instant, inconsistent_node
"Repairing trie tables",
);
}
/// Metrics for tracking trie repair inconsistencies
#[derive(Debug)]
struct RepairTrieMetrics {
account_inconsistencies: Counter,
storage_inconsistencies: Counter,
}
impl RepairTrieMetrics {
fn new() -> Self {
Self {
account_inconsistencies: metrics::counter!(
"db.repair_trie.inconsistencies_found",
"type" => "account"
),
storage_inconsistencies: metrics::counter!(
"db.repair_trie.inconsistencies_found",
"type" => "storage"
),
}
}
}

View File

@@ -0,0 +1,127 @@
//! `reth db settings` command for managing storage settings
use clap::{ArgAction, Parser, Subcommand};
use reth_db_common::DbTool;
use reth_provider::{
providers::ProviderNodeTypes, DBProvider, DatabaseProviderFactory, MetadataProvider,
MetadataWriter, StorageSettings,
};
use crate::common::AccessRights;
/// `reth db settings` subcommand
#[derive(Debug, Parser)]
pub struct Command {
#[command(subcommand)]
command: Subcommands,
}
impl Command {
/// Returns database access rights required for the command.
pub fn access_rights(&self) -> AccessRights {
match self.command {
Subcommands::Get => AccessRights::RO,
Subcommands::Set(_) => AccessRights::RW,
}
}
}
#[derive(Debug, Clone, Copy, Subcommand)]
enum Subcommands {
/// Get current storage settings from database
Get,
/// Set storage settings in database
#[clap(subcommand)]
Set(SetCommand),
}
/// Set storage settings
#[derive(Debug, Clone, Copy, Subcommand)]
#[clap(rename_all = "snake_case")]
pub enum SetCommand {
/// Store receipts in static files instead of the database
ReceiptsInStaticFiles {
#[clap(action(ArgAction::Set))]
value: bool,
},
/// Store transaction senders in static files instead of the database
TransactionSendersInStaticFiles {
#[clap(action(ArgAction::Set))]
value: bool,
},
}
impl Command {
/// Execute the command
pub fn execute<N: ProviderNodeTypes>(self, tool: &DbTool<N>) -> eyre::Result<()> {
match self.command {
Subcommands::Get => self.get(tool),
Subcommands::Set(cmd) => self.set(cmd, tool),
}
}
fn get<N: ProviderNodeTypes>(&self, tool: &DbTool<N>) -> eyre::Result<()> {
// Read storage settings
let provider = tool.provider_factory.provider()?;
let storage_settings = provider.storage_settings()?;
// Display settings
match storage_settings {
Some(settings) => {
println!("Current storage settings:");
println!("{settings:#?}");
}
None => {
println!("No storage settings found.");
}
}
Ok(())
}
fn set<N: ProviderNodeTypes>(&self, cmd: SetCommand, tool: &DbTool<N>) -> eyre::Result<()> {
// Read storage settings
let provider_rw = tool.provider_factory.database_provider_rw()?;
// Destruct settings struct to not miss adding support for new fields
let settings = provider_rw.storage_settings()?;
if settings.is_none() {
println!("No storage settings found, creating new settings.");
}
let mut settings @ StorageSettings {
receipts_in_static_files: _,
transaction_senders_in_static_files: _,
storages_history_in_rocksdb: _,
transaction_hash_numbers_in_rocksdb: _,
account_history_in_rocksdb: _,
} = settings.unwrap_or_else(StorageSettings::legacy);
// Update the setting based on the key
match cmd {
SetCommand::ReceiptsInStaticFiles { value } => {
if settings.receipts_in_static_files == value {
println!("receipts_in_static_files is already set to {}", value);
return Ok(());
}
settings.receipts_in_static_files = value;
println!("Set receipts_in_static_files = {}", value);
}
SetCommand::TransactionSendersInStaticFiles { value } => {
if settings.transaction_senders_in_static_files == value {
println!("transaction_senders_in_static_files is already set to {}", value);
return Ok(());
}
settings.transaction_senders_in_static_files = value;
println!("Set transaction_senders_in_static_files = {}", value);
}
}
// Write updated settings
provider_rw.write_storage_settings(settings)?;
provider_rw.commit()?;
println!("Storage settings updated successfully.");
Ok(())
}
}

View File

@@ -0,0 +1,63 @@
use clap::{Parser, Subcommand};
use reth_db_common::DbTool;
use reth_provider::{providers::ProviderNodeTypes, StaticFileProviderFactory};
use reth_static_file_types::StaticFileSegment;
use std::path::PathBuf;
use tracing::warn;
/// The arguments for the `reth db static-file-header` command
#[derive(Parser, Debug)]
pub struct Command {
#[command(subcommand)]
source: Source,
}
/// Source for locating the static file
#[derive(Subcommand, Debug)]
enum Source {
/// Query by segment and block number
Block {
/// Static file segment
#[arg(value_enum)]
segment: StaticFileSegment,
/// Block number to query
block: u64,
},
/// Query by path to static file
Path {
/// Path to the static file
path: PathBuf,
},
}
impl Command {
/// Execute `db static-file-header` command
pub fn execute<N: ProviderNodeTypes>(self, tool: &DbTool<N>) -> eyre::Result<()> {
let static_file_provider = tool.provider_factory.static_file_provider();
if let Err(err) = static_file_provider.check_consistency(&tool.provider_factory.provider()?)
{
warn!("Error checking consistency of static files: {err}");
}
// Get the provider based on the source
let provider = match self.source {
Source::Path { path } => {
static_file_provider.get_segment_provider_for_path(&path)?.ok_or_else(|| {
eyre::eyre!("Could not find static file segment for path: {}", path.display())
})?
}
Source::Block { segment, block } => {
static_file_provider.get_segment_provider(segment, block)?
}
};
let header = provider.user_header();
println!("Segment: {}", header.segment());
println!("Expected Block Range: {}", header.expected_block_range());
println!("Block Range: {:?}", header.block_range());
println!("Transaction Range: {:?}", header.tx_range());
Ok(())
}
}

View File

@@ -18,6 +18,10 @@ use std::{sync::Arc, time::Duration};
#[derive(Parser, Debug)]
/// The arguments for the `reth db stats` command
pub struct Command {
/// Skip consistency checks for static files.
#[arg(long, default_value_t = false)]
pub(crate) skip_consistency_checks: bool,
/// Show only the total size for static files.
#[arg(long, default_value_t = false)]
detailed_sizes: bool,
@@ -191,10 +195,11 @@ impl Command {
mut segment_config_size,
) = (0, 0, 0, 0, 0, 0);
for (block_range, tx_range) in &ranges {
let fixed_block_range = static_file_provider.find_fixed_range(block_range.start());
for (block_range, header) in &ranges {
let fixed_block_range =
static_file_provider.find_fixed_range(segment, block_range.start());
let jar_provider = static_file_provider
.get_segment_provider(segment, || Some(fixed_block_range), None)?
.get_segment_provider_for_range(segment, || Some(fixed_block_range), None)?
.ok_or_else(|| {
eyre::eyre!("Failed to get segment provider for segment: {}", segment)
})?;
@@ -220,7 +225,7 @@ impl Command {
row.add_cell(Cell::new(segment))
.add_cell(Cell::new(format!("{block_range}")))
.add_cell(Cell::new(
tx_range.map_or("N/A".to_string(), |tx_range| format!("{tx_range}")),
header.tx_range().map_or("N/A".to_string(), |range| format!("{range}")),
))
.add_cell(Cell::new(format!("{columns} x {rows}")));
if self.detailed_sizes {
@@ -270,10 +275,12 @@ impl Command {
let tx_range = {
let start = ranges
.iter()
.find_map(|(_, tx_range)| tx_range.map(|r| r.start()))
.find_map(|(_, header)| header.tx_range().map(|range| range.start()))
.unwrap_or_default();
let end =
ranges.iter().rev().find_map(|(_, tx_range)| tx_range.map(|r| r.end()));
let end = ranges
.iter()
.rev()
.find_map(|(_, header)| header.tx_range().map(|range| range.end()));
end.map(|end| SegmentRangeInclusive::new(start, end))
};

View File

@@ -4,7 +4,7 @@ use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
use clap::{Args, Parser};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_era::execution_types::MAX_BLOCKS_PER_ERA1;
use reth_era::era1::types::execution::MAX_BLOCKS_PER_ERA1;
use reth_era_utils as era1;
use reth_provider::DatabaseProviderFactory;
use std::{path::PathBuf, sync::Arc};

View File

@@ -1,8 +1,9 @@
//! Command that initializes the node from a genesis file.
use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
use alloy_consensus::BlockHeader;
use clap::Parser;
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_provider::BlockHashReader;
use std::sync::Arc;
@@ -22,8 +23,9 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitComman
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
let genesis_block_number = provider_factory.chain_spec().genesis_header().number();
let hash = provider_factory
.block_hash(0)?
.block_hash(genesis_block_number)?
.ok_or_else(|| eyre::eyre!("Genesis hash not found."))?;
info!(target: "reth::cli", hash = ?hash, "Genesis block written");

View File

@@ -1,6 +1,6 @@
//! Command that initializes the node from a genesis file.
use crate::common::{AccessRights, CliHeader, CliNodeTypes, Environment, EnvironmentArgs};
use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
use alloy_consensus::BlockHeader as AlloyBlockHeader;
use alloy_primitives::{Sealable, B256};
use clap::Parser;
@@ -8,7 +8,7 @@ use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_db_common::init::init_from_state_dump;
use reth_node_api::NodePrimitives;
use reth_primitives_traits::{BlockHeader, SealedHeader};
use reth_primitives_traits::{header::HeaderMut, SealedHeader};
use reth_provider::{
BlockNumReader, DBProvider, DatabaseProviderFactory, StaticFileProviderFactory,
StaticFileWriter,
@@ -69,7 +69,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
where
N: CliNodeTypes<
ChainSpec = C::ChainSpec,
Primitives: NodePrimitives<BlockHeader: BlockHeader + CliHeader>,
Primitives: NodePrimitives<BlockHeader: HeaderMut>,
>,
{
info!(target: "reth::cli", "Reth init-state starting");
@@ -110,7 +110,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
static_file_provider.commit()?;
} else if last_block_number > 0 && last_block_number < header.number() {
return Err(eyre::eyre!(
"Data directory should be empty when calling init-state with --without-evm-history."
"Data directory should be empty when calling init-state with --without-evm."
));
}
}

View File

@@ -10,7 +10,7 @@ use reth_node_builder::NodeBuilder;
use reth_node_core::{
args::{
DatabaseArgs, DatadirArgs, DebugArgs, DevArgs, EngineArgs, EraArgs, MetricArgs,
NetworkArgs, PayloadBuilderArgs, PruningArgs, RpcServerArgs, TxPoolArgs,
NetworkArgs, PayloadBuilderArgs, PruningArgs, RpcServerArgs, StaticFilesArgs, TxPoolArgs,
},
node_config::NodeConfig,
version,
@@ -110,6 +110,10 @@ pub struct NodeCommand<C: ChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs
#[command(flatten, next_help_heading = "ERA")]
pub era: EraArgs,
/// All static files related arguments
#[command(flatten, next_help_heading = "Static Files")]
pub static_files: StaticFilesArgs,
/// Additional cli arguments
#[command(flatten, next_help_heading = "Extension")]
pub ext: Ext,
@@ -145,7 +149,7 @@ where
where
L: Launcher<C, Ext>,
{
tracing::info!(target: "reth::cli", version = ?version::version_metadata().short_version, "Starting reth");
tracing::info!(target: "reth::cli", version = ?version::version_metadata().short_version, "Starting {}", version::version_metadata().name_client);
let Self {
datadir,
@@ -162,9 +166,10 @@ where
db,
dev,
pruning,
ext,
engine,
era,
static_files,
ext,
} = self;
// set up node config
@@ -184,6 +189,7 @@ where
pruning,
engine,
era,
static_files,
};
let data_dir = node_config.datadir();

View File

@@ -60,7 +60,7 @@ impl Command {
if self.v5 {
info!("Starting discv5");
let config = Config::builder(self.addr).build();
let (_discv5, updates, _local_enr_discv5) = Discv5::start(&sk, config).await?;
let (_discv5, updates) = Discv5::start(&sk, config).await?;
discv5_updates = Some(updates);
};

View File

@@ -8,7 +8,7 @@ use backon::{ConstantBuilder, Retryable};
use clap::{Parser, Subcommand};
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_util::{get_secret_key, hash_or_num_value_parser};
use reth_cli_util::hash_or_num_value_parser;
use reth_config::Config;
use reth_network::{BlockDownloaderProvider, NetworkConfigBuilder};
use reth_network_p2p::bodies::client::BodiesClient;
@@ -72,7 +72,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
.split();
if result.len() != 1 {
eyre::bail!(
"Invalid number of headers received. Expected: 1. Received: {}",
"Invalid number of bodies received. Expected: 1. Received: {}",
result.len()
)
}
@@ -183,15 +183,13 @@ impl<C: ChainSpecParser> DownloadArgs<C> {
config.peers.trusted_nodes_only = self.network.trusted_only;
let default_secret_key_path = data_dir.p2p_secret();
let secret_key_path =
self.network.p2p_secret_key.clone().unwrap_or(default_secret_key_path);
let p2p_secret_key = get_secret_key(&secret_key_path)?;
let p2p_secret_key = self.network.secret_key(default_secret_key_path)?;
let rlpx_socket = (self.network.addr, self.network.port).into();
let boot_nodes = self.chain.bootnodes().unwrap_or_default();
let net = NetworkConfigBuilder::<N::NetworkPrimitives>::new(p2p_secret_key)
.peer_config(config.peers_config_with_basic_nodes_from_file(None))
.external_ip_resolver(self.network.nat)
.external_ip_resolver(self.network.nat.clone())
.network_id(self.network.network_id)
.boot_nodes(boot_nodes.clone())
.apply(|builder| {

View File

@@ -9,6 +9,7 @@ use clap::Parser;
use eyre::WrapErr;
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_util::cancellation::CancellationToken;
use reth_consensus::FullConsensus;
use reth_evm::{execute::Executor, ConfigureEvm};
use reth_primitives_traits::{format_gas_throughput, BlockBody, GotExpected};
@@ -44,6 +45,10 @@ pub struct Command<C: ChainSpecParser> {
/// Number of tasks to run in parallel
#[arg(long, default_value = "10")]
num_tasks: u64,
/// Continues with execution when an invalid block is encountered and collects these blocks.
#[arg(long)]
skip_invalid_blocks: bool,
}
impl<C: ChainSpecParser> Command<C> {
@@ -61,11 +66,23 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
{
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RO)?;
let provider = provider_factory.database_provider_ro()?;
let components = components(provider_factory.chain_spec());
let min_block = self.from;
let max_block = self.to.unwrap_or(provider.best_block_number()?);
let best_block = DatabaseProviderFactory::database_provider_ro(&provider_factory)?
.best_block_number()?;
let mut max_block = best_block;
if let Some(to) = self.to {
if to > best_block {
warn!(
requested = to,
best_block,
"Requested --to is beyond available chain head; clamping to best block"
);
} else {
max_block = to;
}
};
let total_blocks = max_block - min_block;
let total_gas = calculate_gas_used_from_headers(
@@ -83,7 +100,11 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
}
};
let skip_invalid_blocks = self.skip_invalid_blocks;
let (stats_tx, mut stats_rx) = mpsc::unbounded_channel();
let (info_tx, mut info_rx) = mpsc::unbounded_channel();
let cancellation = CancellationToken::new();
let _guard = cancellation.drop_guard();
let mut tasks = JoinSet::new();
for i in 0..self.num_tasks {
@@ -97,17 +118,40 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
let consensus = components.consensus().clone();
let db_at = db_at.clone();
let stats_tx = stats_tx.clone();
let info_tx = info_tx.clone();
let cancellation = cancellation.clone();
tasks.spawn_blocking(move || {
let mut executor = evm_config.batch_executor(db_at(start_block - 1));
for block in start_block..end_block {
let mut executor_created = Instant::now();
let executor_lifetime = Duration::from_secs(120);
'blocks: for block in start_block..end_block {
if cancellation.is_cancelled() {
// exit if the program is being terminated
break
}
let block = provider_factory
.recovered_block(block.into(), TransactionVariant::NoHash)?
.unwrap();
let result = executor.execute_one(&block)?;
let result = match executor.execute_one(&block) {
Ok(result) => result,
Err(err) => {
if skip_invalid_blocks {
executor = evm_config.batch_executor(db_at(block.number()));
let _ = info_tx.send((block, eyre::Report::new(err)));
continue
}
return Err(err.into())
}
};
if let Err(err) = consensus
.validate_block_post_execution(&block, &result)
.wrap_err_with(|| format!("Failed to validate block {}", block.number()))
.wrap_err_with(|| {
format!("Failed to validate block {} {}", block.number(), block.hash())
})
{
let correct_receipts =
provider_factory.receipts_by_block(block.number().into())?.unwrap();
@@ -143,6 +187,11 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
};
error!(number=?block.number(), ?mismatch, "Gas usage mismatch");
if skip_invalid_blocks {
executor = evm_config.batch_executor(db_at(block.number()));
let _ = info_tx.send((block, err));
continue 'blocks;
}
return Err(err);
}
} else {
@@ -154,9 +203,12 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
}
let _ = stats_tx.send(block.gas_used());
// Reset DB once in a while to avoid OOM
if executor.size_hint() > 1_000_000 {
// Reset DB once in a while to avoid OOM or read tx timeouts
if executor.size_hint() > 1_000_000 ||
executor_created.elapsed() > executor_lifetime
{
executor = evm_config.batch_executor(db_at(block.number()));
executor_created = Instant::now();
}
}
@@ -171,6 +223,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
let mut last_logged_gas = 0;
let mut last_logged_blocks = 0;
let mut last_logged_time = Instant::now();
let mut invalid_blocks = Vec::new();
let mut interval = tokio::time::interval(Duration::from_secs(10));
@@ -180,6 +233,10 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
total_executed_blocks += 1;
total_executed_gas += gas_used;
}
Some((block, err)) = info_rx.recv() => {
error!(?err, block=?block.num_hash(), "Invalid block");
invalid_blocks.push(block.num_hash());
}
result = tasks.join_next() => {
if let Some(result) = result {
if matches!(result, Err(_) | Ok(Err(_))) {
@@ -210,12 +267,25 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
}
}
info!(
start_block = min_block,
end_block = max_block,
throughput=?format_gas_throughput(total_executed_gas, instant.elapsed()),
"Re-executed successfully"
);
if invalid_blocks.is_empty() {
info!(
start_block = min_block,
end_block = max_block,
%total_executed_blocks,
throughput=?format_gas_throughput(total_executed_gas, instant.elapsed()),
"Re-executed successfully"
);
} else {
info!(
start_block = min_block,
end_block = max_block,
%total_executed_blocks,
invalid_block_count = invalid_blocks.len(),
?invalid_blocks,
throughput=?format_gas_throughput(total_executed_gas, instant.elapsed()),
"Re-executed with invalid blocks"
);
}
Ok(())
}

View File

@@ -1,10 +1,9 @@
//! Database debugging tool
use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
use clap::Parser;
use itertools::Itertools;
use reth_chainspec::EthChainSpec;
use reth_cli::chainspec::ChainSpecParser;
use reth_db::{mdbx::tx::Tx, static_file::iter_static_files, DatabaseError};
use reth_db::{mdbx::tx::Tx, DatabaseError};
use reth_db_api::{
tables,
transaction::{DbTx, DbTxMut},
@@ -15,7 +14,9 @@ use reth_db_common::{
};
use reth_node_api::{HeaderTy, ReceiptTy, TxTy};
use reth_node_core::args::StageEnum;
use reth_provider::{DBProvider, DatabaseProviderFactory, StaticFileProviderFactory, TrieWriter};
use reth_provider::{
DBProvider, DatabaseProviderFactory, StaticFileProviderFactory, StaticFileWriter, TrieWriter,
};
use reth_prune::PruneSegment;
use reth_stages::StageId;
use reth_static_file_types::StaticFileSegment;
@@ -44,21 +45,48 @@ impl<C: ChainSpecParser> Command<C> {
StageEnum::Headers => Some(StaticFileSegment::Headers),
StageEnum::Bodies => Some(StaticFileSegment::Transactions),
StageEnum::Execution => Some(StaticFileSegment::Receipts),
StageEnum::Senders => Some(StaticFileSegment::TransactionSenders),
_ => None,
};
// Delete static file segment data before inserting the genesis header below
// Calling `StaticFileProviderRW::prune_*` will instruct the writer to prune rows only
// when `StaticFileProviderRW::commit` is called. We need to do that instead of
// deleting the jar files, otherwise if the task were to be interrupted after we
// have deleted them, BUT before we have committed the checkpoints to the database, we'd
// lose essential data.
if let Some(static_file_segment) = static_file_segment {
let static_file_provider = tool.provider_factory.static_file_provider();
let static_files = iter_static_files(static_file_provider.directory())?;
if let Some(segment_static_files) = static_files.get(&static_file_segment) {
// Delete static files from the highest to the lowest block range
for (block_range, _) in segment_static_files
.iter()
.sorted_by_key(|(block_range, _)| block_range.start())
.rev()
{
static_file_provider.delete_jar(static_file_segment, block_range.start())?;
if let Some(highest_block) =
static_file_provider.get_highest_static_file_block(static_file_segment)
{
let mut writer = static_file_provider.latest_writer(static_file_segment)?;
match static_file_segment {
StaticFileSegment::Headers => {
// Prune all headers leaving genesis intact.
writer.prune_headers(highest_block)?;
}
StaticFileSegment::Transactions => {
let to_delete = static_file_provider
.get_highest_static_file_tx(static_file_segment)
.map(|tx_num| tx_num + 1)
.unwrap_or_default();
writer.prune_transactions(to_delete, 0)?;
}
StaticFileSegment::Receipts => {
let to_delete = static_file_provider
.get_highest_static_file_tx(static_file_segment)
.map(|tx_num| tx_num + 1)
.unwrap_or_default();
writer.prune_receipts(to_delete, 0)?;
}
StaticFileSegment::TransactionSenders => {
let to_delete = static_file_provider
.get_highest_static_file_tx(static_file_segment)
.map(|tx_num| tx_num + 1)
.unwrap_or_default();
writer.prune_transaction_senders(to_delete, 0)?;
}
}
}
}

View File

@@ -9,7 +9,7 @@ use reth_evm::ConfigureEvm;
use reth_node_builder::NodeTypesWithDB;
use reth_node_core::dirs::{ChainPath, DataDirPath};
use reth_provider::{
providers::{ProviderNodeTypes, StaticFileProvider},
providers::{ProviderNodeTypes, RocksDBProvider, StaticFileProvider},
DatabaseProviderFactory, ProviderFactory,
};
use reth_stages::{stages::ExecutionStage, Stage, StageCheckpoint, UnwindInput};
@@ -42,7 +42,8 @@ where
Arc::new(output_db),
db_tool.chain(),
StaticFileProvider::read_write(output_datadir.static_files())?,
),
RocksDBProvider::builder(output_datadir.rocksdb()).build()?,
)?,
to,
from,
evm_config,

View File

@@ -6,7 +6,7 @@ use reth_db_api::{database::Database, table::TableImporter, tables};
use reth_db_common::DbTool;
use reth_node_core::dirs::{ChainPath, DataDirPath};
use reth_provider::{
providers::{ProviderNodeTypes, StaticFileProvider},
providers::{ProviderNodeTypes, RocksDBProvider, StaticFileProvider},
DatabaseProviderFactory, ProviderFactory,
};
use reth_stages::{stages::AccountHashingStage, Stage, StageCheckpoint, UnwindInput};
@@ -39,7 +39,8 @@ pub(crate) async fn dump_hashing_account_stage<N: ProviderNodeTypes<DB = Arc<Dat
Arc::new(output_db),
db_tool.chain(),
StaticFileProvider::read_write(output_datadir.static_files())?,
),
RocksDBProvider::builder(output_datadir.rocksdb()).build()?,
)?,
to,
from,
)?;

View File

@@ -5,7 +5,7 @@ use reth_db_api::{database::Database, table::TableImporter, tables};
use reth_db_common::DbTool;
use reth_node_core::dirs::{ChainPath, DataDirPath};
use reth_provider::{
providers::{ProviderNodeTypes, StaticFileProvider},
providers::{ProviderNodeTypes, RocksDBProvider, StaticFileProvider},
DatabaseProviderFactory, ProviderFactory,
};
use reth_stages::{stages::StorageHashingStage, Stage, StageCheckpoint, UnwindInput};
@@ -29,7 +29,8 @@ pub(crate) async fn dump_hashing_storage_stage<N: ProviderNodeTypes<DB = Arc<Dat
Arc::new(output_db),
db_tool.chain(),
StaticFileProvider::read_write(output_datadir.static_files())?,
),
RocksDBProvider::builder(output_datadir.rocksdb()).build()?,
)?,
to,
from,
)?;

View File

@@ -12,7 +12,7 @@ use reth_evm::ConfigureEvm;
use reth_exex::ExExManagerHandle;
use reth_node_core::dirs::{ChainPath, DataDirPath};
use reth_provider::{
providers::{ProviderNodeTypes, StaticFileProvider},
providers::{ProviderNodeTypes, RocksDBProvider, StaticFileProvider},
DatabaseProviderFactory, ProviderFactory,
};
use reth_stages::{
@@ -62,7 +62,8 @@ where
Arc::new(output_db),
db_tool.chain(),
StaticFileProvider::read_write(output_datadir.static_files())?,
),
RocksDBProvider::builder(output_datadir.rocksdb()).build()?,
)?,
to,
from,
)?;

View File

@@ -84,6 +84,9 @@ pub struct Command<C: ChainSpecParser> {
/// Commits the changes in the database. WARNING: potentially destructive.
///
/// Useful when you want to run diagnostics on the database.
///
/// NOTE: This flag is currently required for the headers, bodies, and execution stages because
/// they use static files and must commit to properly unwind and run.
// TODO: We should consider allowing to run hooks at the end of the stage run,
// e.g. query the DB size, or any table data.
#[arg(long, short)]
@@ -105,6 +108,14 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
Comp: CliNodeComponents<N>,
F: FnOnce(Arc<C::ChainSpec>) -> Comp,
{
// Quit early if the stages requires a commit and `--commit` is not provided.
if self.requires_commit() && !self.commit {
return Err(eyre::eyre!(
"The stage {} requires overwriting existing static files and must commit, but `--commit` was not provided. Please pass `--commit` and try again.",
self.stage.to_string()
));
}
// Raise the fd limit of the process.
// Does not do anything on windows.
let _ = fdlimit::raise_fd_limit();
@@ -383,4 +394,13 @@ impl<C: ChainSpecParser> Command<C> {
pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
Some(&self.env.chain)
}
/// Returns whether or not the configured stage requires committing.
///
/// This is the case for stages that mainly modify static files, as there is no way to unwind
/// these stages without committing anyways. This is because static files do not have
/// transactions and we cannot change the view of headers without writing.
pub fn requires_commit(&self) -> bool {
matches!(self.stage, StageEnum::Headers | StageEnum::Bodies | StageEnum::Execution)
}
}

View File

@@ -97,6 +97,57 @@ impl CliRunner {
command_res
}
/// Executes a command in a blocking context with access to `CliContext`.
///
/// See [`Runtime::spawn_blocking`](tokio::runtime::Runtime::spawn_blocking).
pub fn run_blocking_command_until_exit<F, E>(
self,
command: impl FnOnce(CliContext) -> F + Send + 'static,
) -> Result<(), E>
where
F: Future<Output = Result<(), E>> + Send + 'static,
E: Send + Sync + From<std::io::Error> + From<reth_tasks::PanickedTaskError> + 'static,
{
let AsyncCliRunner { context, mut task_manager, tokio_runtime } =
AsyncCliRunner::new(self.tokio_runtime);
// Spawn the command on the blocking thread pool
let handle = tokio_runtime.handle().clone();
let command_handle =
tokio_runtime.handle().spawn_blocking(move || handle.block_on(command(context)));
// Wait for the command to complete or ctrl-c
let command_res = tokio_runtime.block_on(run_to_completion_or_panic(
&mut task_manager,
run_until_ctrl_c(
async move { command_handle.await.expect("Failed to join blocking task") },
),
));
if command_res.is_err() {
error!(target: "reth::cli", "shutting down due to error");
} else {
debug!(target: "reth::cli", "shutting down gracefully");
task_manager.graceful_shutdown_with_timeout(Duration::from_secs(5));
}
// Shutdown the runtime on a separate thread
let (tx, rx) = mpsc::channel();
std::thread::Builder::new()
.name("tokio-runtime-shutdown".to_string())
.spawn(move || {
drop(tokio_runtime);
let _ = tx.send(());
})
.unwrap();
let _ = rx.recv_timeout(Duration::from_secs(5)).inspect_err(|err| {
debug!(target: "reth::cli", %err, "tokio runtime shutdown timed out");
});
command_res
}
/// Executes a regular future until completion or until external signal received.
pub fn run_until_ctrl_c<F, E>(self, fut: F) -> Result<(), E>
where

View File

@@ -42,6 +42,9 @@ jemalloc = ["dep:tikv-jemallocator"]
# Enables jemalloc profiling features
jemalloc-prof = ["jemalloc", "tikv-jemallocator?/profiling"]
# Enables unprefixed malloc (reproducible builds support)
jemalloc-unprefixed = ["jemalloc", "tikv-jemallocator?/unprefixed_malloc_on_supported_platforms"]
# Wraps the selected allocator in the tracy profiling allocator
tracy-allocator = ["dep:tracy-client"]

View File

@@ -0,0 +1,103 @@
//! Thread-safe cancellation primitives for cooperative task cancellation.
use std::sync::{
atomic::{AtomicBool, Ordering},
Arc,
};
/// A thread-safe cancellation token that can be shared across threads.
///
/// This token allows cooperative cancellation by providing a way to signal
/// cancellation and check cancellation status. The token can be cloned and
/// shared across multiple threads, with all clones sharing the same cancellation state.
///
/// # Example
///
/// ```
/// use reth_cli_util::cancellation::CancellationToken;
/// use std::{thread, time::Duration};
///
/// let token = CancellationToken::new();
/// let worker_token = token.clone();
///
/// let handle = thread::spawn(move || {
/// while !worker_token.is_cancelled() {
/// // Do work...
/// thread::sleep(Duration::from_millis(100));
/// }
/// });
///
/// // Cancel from main thread
/// token.cancel();
/// handle.join().unwrap();
/// ```
#[derive(Clone, Debug)]
pub struct CancellationToken {
cancelled: Arc<AtomicBool>,
}
impl CancellationToken {
/// Creates a new cancellation token in the non-cancelled state.
pub fn new() -> Self {
Self { cancelled: Arc::new(AtomicBool::new(false)) }
}
/// Signals cancellation to all holders of this token and its clones.
///
/// Once cancelled, the token cannot be reset. This operation is thread-safe
/// and can be called multiple times without issue.
pub fn cancel(&self) {
self.cancelled.store(true, Ordering::Release);
}
/// Checks whether cancellation has been requested.
///
/// Returns `true` if [`cancel`](Self::cancel) has been called on this token
/// or any of its clones.
pub fn is_cancelled(&self) -> bool {
self.cancelled.load(Ordering::Relaxed)
}
/// Creates a guard that automatically cancels this token when dropped.
///
/// This is useful for ensuring cancellation happens when a scope exits,
/// either normally or via panic.
///
/// # Example
///
/// ```
/// use reth_cli_util::cancellation::CancellationToken;
///
/// let token = CancellationToken::new();
/// {
/// let _guard = token.drop_guard();
/// assert!(!token.is_cancelled());
/// // Guard dropped here, triggering cancellation
/// }
/// assert!(token.is_cancelled());
/// ```
pub fn drop_guard(&self) -> CancellationGuard {
CancellationGuard { token: self.clone() }
}
}
impl Default for CancellationToken {
fn default() -> Self {
Self::new()
}
}
/// A guard that cancels its associated [`CancellationToken`] when dropped.
///
/// Created by calling [`CancellationToken::drop_guard`]. When this guard is dropped,
/// it automatically calls [`cancel`](CancellationToken::cancel) on the token.
#[derive(Debug)]
pub struct CancellationGuard {
token: CancellationToken,
}
impl Drop for CancellationGuard {
fn drop(&mut self) {
self.token.cancel();
}
}

View File

@@ -9,10 +9,11 @@
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod allocator;
pub mod cancellation;
/// Helper function to load a secret key from a file.
pub mod load_secret_key;
pub use load_secret_key::get_secret_key;
pub use load_secret_key::{get_secret_key, parse_secret_key_from_hex};
/// Cli parsers functions.
pub mod parsers;

View File

@@ -30,6 +30,10 @@ pub enum SecretKeyError {
/// Path to the secret key file.
secret_file: PathBuf,
},
/// Invalid hex string format.
#[error("invalid hex string: {0}")]
InvalidHexString(String),
}
/// Attempts to load a [`SecretKey`] from a specified path. If no file exists there, then it
@@ -60,3 +64,75 @@ pub fn get_secret_key(secret_key_path: &Path) -> Result<SecretKey, SecretKeyErro
}),
}
}
/// Parses a [`SecretKey`] from a hex string.
///
/// The hex string can optionally start with "0x".
pub fn parse_secret_key_from_hex(hex_str: &str) -> Result<SecretKey, SecretKeyError> {
// Remove "0x" prefix if present
let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
// Decode the hex string
let bytes = alloy_primitives::hex::decode(hex_str)
.map_err(|e| SecretKeyError::InvalidHexString(e.to_string()))?;
// Parse into SecretKey
SecretKey::from_slice(&bytes).map_err(SecretKeyError::SecretKeyDecodeError)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_secret_key_from_hex_without_prefix() {
// Valid 32-byte hex string (64 characters)
let hex = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_ok());
let secret_key = result.unwrap();
assert_eq!(alloy_primitives::hex::encode(secret_key.secret_bytes()), hex);
}
#[test]
fn test_parse_secret_key_from_hex_with_0x_prefix() {
// Valid 32-byte hex string with 0x prefix
let hex = "0x4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_ok());
let secret_key = result.unwrap();
let expected = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
assert_eq!(alloy_primitives::hex::encode(secret_key.secret_bytes()), expected);
}
#[test]
fn test_parse_secret_key_from_hex_invalid_length() {
// Invalid length (not 32 bytes)
let hex = "4c0883a69102937d";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_err());
}
#[test]
fn test_parse_secret_key_from_hex_invalid_chars() {
// Invalid hex characters
let hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_err());
if let Err(SecretKeyError::InvalidHexString(_)) = result {
// Expected error type
} else {
panic!("Expected InvalidHexString error");
}
}
#[test]
fn test_parse_secret_key_from_hex_empty() {
let hex = "";
let result = parse_secret_key_from_hex(hex);
assert!(result.is_err());
}
}

View File

@@ -31,6 +31,16 @@ pub fn parse_duration_from_secs_or_ms(
}
}
/// Helper to format a [Duration] to the format that can be parsed by
/// [`parse_duration_from_secs_or_ms`].
pub fn format_duration_as_secs_or_ms(duration: Duration) -> String {
if duration.as_millis().is_multiple_of(1000) {
format!("{}", duration.as_secs())
} else {
format!("{}ms", duration.as_millis())
}
}
/// Parse [`BlockHashOrNumber`]
pub fn hash_or_num_value_parser(value: &str) -> eyre::Result<BlockHashOrNumber, eyre::Error> {
match B256::from_str(value) {

View File

@@ -126,7 +126,8 @@ pub fn install() {
libc::sigaltstack(&raw const alt_stack, ptr::null_mut());
let mut sa: libc::sigaction = mem::zeroed();
sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
sa.sa_sigaction =
print_stack_trace as unsafe extern "C" fn(libc::c_int) as libc::sighandler_t;
sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
libc::sigemptyset(&raw mut sa.sa_mask);
libc::sigaction(libc::SIGSEGV, &raw const sa, ptr::null_mut());

View File

@@ -15,6 +15,7 @@ workspace = true
reth-network-types.workspace = true
reth-prune-types.workspace = true
reth-stages-types.workspace = true
reth-static-file-types.workspace = true
# serde
serde = { workspace = true, optional = true }
@@ -22,7 +23,7 @@ humantime-serde = { workspace = true, optional = true }
# toml
toml = { workspace = true, optional = true }
eyre = { workspace = true, optional = true }
eyre.workspace = true
# value objects
url.workspace = true
@@ -31,7 +32,6 @@ url.workspace = true
serde = [
"dep:serde",
"dep:toml",
"dep:eyre",
"dep:humantime-serde",
"reth-network-types/serde",
"reth-prune-types/serde",

View File

@@ -2,7 +2,9 @@
use reth_network_types::{PeersConfig, SessionsConfig};
use reth_prune_types::PruneModes;
use reth_stages_types::ExecutionStageThresholds;
use reth_static_file_types::StaticFileSegment;
use std::{
collections::HashMap,
path::{Path, PathBuf},
time::Duration,
};
@@ -29,6 +31,9 @@ pub struct Config {
pub peers: PeersConfig,
/// Configuration for peer sessions.
pub sessions: SessionsConfig,
/// Configuration for static files.
#[cfg_attr(feature = "serde", serde(default))]
pub static_files: StaticFilesConfig,
}
impl Config {
@@ -411,6 +416,77 @@ impl EtlConfig {
}
}
/// Static files configuration.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct StaticFilesConfig {
/// Number of blocks per file for each segment.
pub blocks_per_file: BlocksPerFileConfig,
}
/// Configuration for the number of blocks per file for each segment.
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(default))]
pub struct BlocksPerFileConfig {
/// Number of blocks per file for the headers segment.
pub headers: Option<u64>,
/// Number of blocks per file for the transactions segment.
pub transactions: Option<u64>,
/// Number of blocks per file for the receipts segment.
pub receipts: Option<u64>,
/// Number of blocks per file for the transaction senders segment.
pub transaction_senders: Option<u64>,
}
impl StaticFilesConfig {
/// Validates the static files configuration.
///
/// Returns an error if any blocks per file value is zero.
pub fn validate(&self) -> eyre::Result<()> {
let BlocksPerFileConfig { headers, transactions, receipts, transaction_senders } =
self.blocks_per_file;
eyre::ensure!(headers != Some(0), "Headers segment blocks per file must be greater than 0");
eyre::ensure!(
transactions != Some(0),
"Transactions segment blocks per file must be greater than 0"
);
eyre::ensure!(
receipts != Some(0),
"Receipts segment blocks per file must be greater than 0"
);
eyre::ensure!(
transaction_senders != Some(0),
"Transaction senders segment blocks per file must be greater than 0"
);
Ok(())
}
/// Converts the blocks per file configuration into a [`HashMap`] per segment.
pub fn as_blocks_per_file_map(&self) -> HashMap<StaticFileSegment, u64> {
let BlocksPerFileConfig { headers, transactions, receipts, transaction_senders } =
self.blocks_per_file;
let mut map = HashMap::new();
// Iterating over all possible segments allows us to do an exhaustive match here,
// to not forget to configure new segments in the future.
for segment in StaticFileSegment::iter() {
let blocks_per_file = match segment {
StaticFileSegment::Headers => headers,
StaticFileSegment::Transactions => transactions,
StaticFileSegment::Receipts => receipts,
StaticFileSegment::TransactionSenders => transaction_senders,
};
if let Some(blocks_per_file) = blocks_per_file {
map.insert(segment, blocks_per_file);
}
}
map
}
}
/// History stage configuration.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
@@ -455,8 +531,12 @@ impl PruneConfig {
self.segments.receipts.is_some() || !self.segments.receipts_log_filter.is_empty()
}
/// Merges another `PruneConfig` into this one, taking values from the other config if and only
/// if the corresponding value in this config is not set.
/// Merges values from `other` into `self`.
/// - `Option<PruneMode>` fields: set from `other` only if `self` is `None`.
/// - `block_interval`: set from `other` only if `self.block_interval ==
/// DEFAULT_BLOCK_INTERVAL`.
/// - `merkle_changesets`: always set from `other`.
/// - `receipts_log_filter`: set from `other` only if `self` is empty and `other` is non-empty.
pub fn merge(&mut self, other: Self) {
let Self {
block_interval,
@@ -485,7 +565,7 @@ impl PruneConfig {
self.segments.account_history = self.segments.account_history.or(account_history);
self.segments.storage_history = self.segments.storage_history.or(storage_history);
self.segments.bodies_history = self.segments.bodies_history.or(bodies_history);
// Merkle changesets is not optional, so we just replace it if provided
// Merkle changesets is not optional; always take the value from `other`
self.segments.merkle_changesets = merkle_changesets;
if self.segments.receipts_log_filter.0.is_empty() && !receipts_log_filter.0.is_empty() {

View File

@@ -1,8 +1,6 @@
//! Collection of methods for block validation.
use alloy_consensus::{
constants::MAXIMUM_EXTRA_DATA_SIZE, BlockHeader as _, Transaction, EMPTY_OMMER_ROOT_HASH,
};
use alloy_consensus::{BlockHeader as _, Transaction, EMPTY_OMMER_ROOT_HASH};
use alloy_eips::{eip4844::DATA_GAS_PER_BLOB, eip7840::BlobParams};
use reth_chainspec::{EthChainSpec, EthereumHardfork, EthereumHardforks};
use reth_consensus::{ConsensusError, TxGasLimitTooHighErr};
@@ -225,13 +223,9 @@ where
/// Validates that the EIP-4844 header fields exist and conform to the spec. This ensures that:
///
/// * `blob_gas_used` exists as a header field
/// * `excess_blob_gas` exists as a header field
/// * `parent_beacon_block_root` exists as a header field
/// * `blob_gas_used` is a multiple of `DATA_GAS_PER_BLOB`
/// * `excess_blob_gas` is a multiple of `DATA_GAS_PER_BLOB`
/// * `blob_gas_used` doesn't exceed the max allowed blob gas based on the given params
///
/// Note: This does not enforce any restrictions on `blob_gas_used`
pub fn validate_4844_header_standalone<H: BlockHeader>(
header: &H,
blob_params: BlobParams,
@@ -264,9 +258,12 @@ pub fn validate_4844_header_standalone<H: BlockHeader>(
/// From yellow paper: extraData: An arbitrary byte array containing data relevant to this block.
/// This must be 32 bytes or fewer; formally Hx.
#[inline]
pub fn validate_header_extra_data<H: BlockHeader>(header: &H) -> Result<(), ConsensusError> {
pub fn validate_header_extra_data<H: BlockHeader>(
header: &H,
max_size: usize,
) -> Result<(), ConsensusError> {
let extra_data_len = header.extra_data().len();
if extra_data_len > MAXIMUM_EXTRA_DATA_SIZE {
if extra_data_len > max_size {
Err(ConsensusError::ExtraDataExceedsMax { len: extra_data_len })
} else {
Ok(())
@@ -282,20 +279,28 @@ pub fn validate_against_parent_hash_number<H: BlockHeader>(
header: &H,
parent: &SealedHeader<H>,
) -> Result<(), ConsensusError> {
// Parent number is consistent.
if parent.number() + 1 != header.number() {
return Err(ConsensusError::ParentBlockNumberMismatch {
parent_block_number: parent.number(),
block_number: header.number(),
})
}
if parent.hash() != header.parent_hash() {
return Err(ConsensusError::ParentHashMismatch(
GotExpected { got: header.parent_hash(), expected: parent.hash() }.into(),
))
}
let Some(parent_number) = parent.number().checked_add(1) else {
// parent block already reached the maximum
return Err(ConsensusError::ParentBlockNumberMismatch {
parent_block_number: parent.number(),
block_number: u64::MAX,
})
};
// Parent number is consistent.
if parent_number != header.number() {
return Err(ConsensusError::ParentBlockNumberMismatch {
parent_block_number: parent.number(),
block_number: header.number(),
})
}
Ok(())
}
@@ -330,7 +335,7 @@ pub fn validate_against_parent_eip1559_base_fee<ChainSpec: EthChainSpec + Ethere
Ok(())
}
/// Validates the timestamp against the parent to make sure it is in the past.
/// Validates that the block timestamp is greater than the parent block timestamp.
#[inline]
pub fn validate_against_parent_timestamp<H: BlockHeader>(
header: &H,
@@ -503,4 +508,21 @@ mod tests {
}))
);
}
#[test]
fn validate_header_extra_data_with_custom_limit() {
// Test with default 32 bytes - should pass
let header_32 = Header { extra_data: Bytes::from(vec![0; 32]), ..Default::default() };
assert!(validate_header_extra_data(&header_32, 32).is_ok());
// Test exceeding default - should fail
let header_33 = Header { extra_data: Bytes::from(vec![0; 33]), ..Default::default() };
assert_eq!(
validate_header_extra_data(&header_33, 32),
Err(ConsensusError::ExtraDataExceedsMax { len: 33 })
);
// Test with custom larger limit - should pass
assert!(validate_header_extra_data(&header_33, 64).is_ok());
}
}

View File

@@ -16,7 +16,7 @@ use alloy_consensus::Header;
use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256};
use reth_execution_types::BlockExecutionResult;
use reth_primitives_traits::{
constants::{MAXIMUM_GAS_LIMIT_BLOCK, MINIMUM_GAS_LIMIT},
constants::{GAS_LIMIT_BOUND_DIVISOR, MAXIMUM_GAS_LIMIT_BLOCK, MINIMUM_GAS_LIMIT},
transaction::error::InvalidTransactionError,
Block, GotExpected, GotExpectedBoxed, NodePrimitives, RecoveredBlock, SealedBlock,
SealedHeader,
@@ -349,7 +349,7 @@ pub enum ConsensusError {
},
/// Error when the child gas limit exceeds the maximum allowed increase.
#[error("child gas_limit {child_gas_limit} max increase is {parent_gas_limit}/1024")]
#[error("child gas_limit {child_gas_limit} exceeds the max allowed increase ({parent_gas_limit}/{GAS_LIMIT_BOUND_DIVISOR})")]
GasLimitInvalidIncrease {
/// The parent gas limit.
parent_gas_limit: u64,
@@ -378,7 +378,7 @@ pub enum ConsensusError {
},
/// Error when the child gas limit exceeds the maximum allowed decrease.
#[error("child gas_limit {child_gas_limit} max decrease is {parent_gas_limit}/1024")]
#[error("child gas_limit {child_gas_limit} is below the max allowed decrease ({parent_gas_limit}/{GAS_LIMIT_BOUND_DIVISOR})")]
GasLimitInvalidDecrease {
/// The parent gas limit.
parent_gas_limit: u64,

View File

@@ -1,5 +1,5 @@
use crate::BlockProvider;
use alloy_provider::{Network, Provider, ProviderBuilder};
use alloy_provider::{ConnectionConfig, Network, Provider, ProviderBuilder, WebSocketConfig};
use alloy_transport::TransportResult;
use futures::{Stream, StreamExt};
use reth_node_api::Block;
@@ -25,7 +25,19 @@ impl<N: Network, PrimitiveBlock> RpcBlockProvider<N, PrimitiveBlock> {
convert: impl Fn(N::BlockResponse) -> PrimitiveBlock + Send + Sync + 'static,
) -> eyre::Result<Self> {
Ok(Self {
provider: Arc::new(ProviderBuilder::default().connect(rpc_url).await?),
provider: Arc::new(
ProviderBuilder::default()
.connect_with_config(
rpc_url,
ConnectionConfig::default().with_max_retries(u32::MAX).with_ws_config(
WebSocketConfig::default()
// allow larger messages/frames for big blocks
.max_frame_size(Some(128 * 1024 * 1024))
.max_message_size(Some(128 * 1024 * 1024)),
),
)
.await?,
),
url: rpc_url.to_string(),
convert: Arc::new(convert),
})
@@ -61,34 +73,42 @@ where
type Block = PrimitiveBlock;
async fn subscribe_blocks(&self, tx: Sender<Self::Block>) {
let Ok(mut stream) = self.full_block_stream().await.inspect_err(|err| {
warn!(
target: "consensus::debug-client",
%err,
url=%self.url,
"Failed to subscribe to blocks",
);
}) else {
return
};
loop {
let Ok(mut stream) = self.full_block_stream().await.inspect_err(|err| {
warn!(
target: "consensus::debug-client",
%err,
url=%self.url,
"Failed to subscribe to blocks",
);
}) else {
return
};
while let Some(res) = stream.next().await {
match res {
Ok(block) => {
if tx.send((self.convert)(block)).await.is_err() {
// Channel closed.
break;
while let Some(res) = stream.next().await {
match res {
Ok(block) => {
if tx.send((self.convert)(block)).await.is_err() {
// Channel closed.
break;
}
}
Err(err) => {
warn!(
target: "consensus::debug-client",
%err,
url=%self.url,
"Failed to fetch a block",
);
}
}
Err(err) => {
warn!(
target: "consensus::debug-client",
%err,
url=%self.url,
"Failed to fetch a block",
);
}
}
// if stream terminated we want to re-establish it again
debug!(
target: "consensus::debug-client",
url=%self.url,
"Re-estbalishing block subscription",
);
}
}

View File

@@ -3,13 +3,12 @@
use node::NodeTestContext;
use reth_chainspec::ChainSpec;
use reth_db::{test_utils::TempDatabase, DatabaseEnv};
use reth_engine_local::LocalPayloadAttributesBuilder;
use reth_network_api::test_utils::PeersHandleProvider;
use reth_node_builder::{
components::NodeComponentsBuilder,
rpc::{EngineValidatorAddOn, RethRpcAddOns},
FullNodeTypesAdapter, Node, NodeAdapter, NodeComponents, NodeTypes, NodeTypesWithDBAdapter,
PayloadAttributesBuilder, PayloadTypes,
PayloadTypes,
};
use reth_provider::providers::{BlockchainProvider, NodeTypesForProvider};
use reth_tasks::TaskManager;
@@ -54,8 +53,6 @@ pub async fn setup<N>(
) -> eyre::Result<(Vec<NodeHelperType<N>>, TaskManager, Wallet)>
where
N: NodeBuilderHelper,
LocalPayloadAttributesBuilder<N::ChainSpec>:
PayloadAttributesBuilder<<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes>,
{
E2ETestSetupBuilder::new(num_nodes, chain_spec, attributes_generator)
.with_node_config_modifier(move |config| config.set_dev(is_dev))
@@ -77,8 +74,6 @@ pub async fn setup_engine<N>(
)>
where
N: NodeBuilderHelper,
LocalPayloadAttributesBuilder<N::ChainSpec>:
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
{
setup_engine_with_connection::<N>(
num_nodes,
@@ -106,8 +101,6 @@ pub async fn setup_engine_with_connection<N>(
)>
where
N: NodeBuilderHelper,
LocalPayloadAttributesBuilder<N::ChainSpec>:
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
{
E2ETestSetupBuilder::new(num_nodes, chain_spec, attributes_generator)
.with_tree_config_modifier(move |_| tree_config.clone())
@@ -160,13 +153,10 @@ where
>,
ChainSpec: From<ChainSpec> + Clone,
>,
LocalPayloadAttributesBuilder<Self::ChainSpec>:
PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>,
{
}
impl<T> NodeBuilderHelper for T
where
impl<T> NodeBuilderHelper for T where
Self: Default
+ NodeTypesForProvider<
Payload: PayloadTypes<
@@ -187,8 +177,6 @@ where
Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
>,
ChainSpec: From<ChainSpec> + Clone,
>,
LocalPayloadAttributesBuilder<Self::ChainSpec>:
PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>,
>
{
}

View File

@@ -4,18 +4,19 @@
//! configurations through closures that modify `NodeConfig` and `TreeConfig`.
use crate::{node::NodeTestContext, wallet::Wallet, NodeBuilderHelper, NodeHelperType, TmpDB};
use futures_util::future::TryJoinAll;
use reth_chainspec::EthChainSpec;
use reth_engine_local::LocalPayloadAttributesBuilder;
use reth_node_builder::{
EngineNodeLauncher, NodeBuilder, NodeConfig, NodeHandle, NodeTypes, NodeTypesWithDBAdapter,
PayloadAttributesBuilder, PayloadTypes,
PayloadTypes,
};
use reth_node_core::args::{DiscoveryArgs, NetworkArgs, RpcServerArgs};
use reth_primitives_traits::AlloyBlockHeader;
use reth_provider::providers::BlockchainProvider;
use reth_rpc_server_types::RpcModuleSelection;
use reth_tasks::TaskManager;
use std::sync::Arc;
use tracing::{span, Level};
use tracing::{span, Instrument, Level};
/// Type alias for tree config modifier closure
type TreeConfigModifier =
@@ -37,8 +38,6 @@ where
+ Sync
+ Copy
+ 'static,
LocalPayloadAttributesBuilder<N::ChainSpec>:
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
{
num_nodes: usize,
chain_spec: Arc<N::ChainSpec>,
@@ -56,8 +55,6 @@ where
+ Sync
+ Copy
+ 'static,
LocalPayloadAttributesBuilder<N::ChainSpec>:
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
{
/// Creates a new builder with the required parameters.
pub fn new(num_nodes: usize, chain_spec: Arc<N::ChainSpec>, attributes_generator: F) -> Self {
@@ -122,66 +119,71 @@ where
reth_node_api::TreeConfig::default()
};
let mut nodes: Vec<NodeTestContext<_, _>> = Vec::with_capacity(self.num_nodes);
let mut nodes = (0..self.num_nodes)
.map(async |idx| {
// Create base node config
let base_config = NodeConfig::new(self.chain_spec.clone())
.with_network(network_config.clone())
.with_unused_ports()
.with_rpc(
RpcServerArgs::default()
.with_unused_ports()
.with_http()
.with_http_api(RpcModuleSelection::All),
);
// Apply node config modifier if present
let node_config = if let Some(modifier) = &self.node_config_modifier {
modifier(base_config)
} else {
base_config
};
let span = span!(Level::INFO, "node", idx);
let node = N::default();
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
.testing_node(exec.clone())
.with_types_and_provider::<N, BlockchainProvider<_>>()
.with_components(node.components_builder())
.with_add_ons(node.add_ons())
.launch_with_fn(|builder| {
let launcher = EngineNodeLauncher::new(
builder.task_executor().clone(),
builder.config().datadir(),
tree_config.clone(),
);
builder.launch_with(launcher)
})
.instrument(span)
.await?;
let node = NodeTestContext::new(node, self.attributes_generator).await?;
let genesis_number = self.chain_spec.genesis_header().number();
let genesis = node.block_hash(genesis_number);
node.update_forkchoice(genesis, genesis).await?;
eyre::Ok(node)
})
.collect::<TryJoinAll<_>>()
.await?;
for idx in 0..self.num_nodes {
// Create base node config
let base_config = NodeConfig::new(self.chain_spec.clone())
.with_network(network_config.clone())
.with_unused_ports()
.with_rpc(
RpcServerArgs::default()
.with_unused_ports()
.with_http()
.with_http_api(RpcModuleSelection::All),
);
// Apply node config modifier if present
let node_config = if let Some(modifier) = &self.node_config_modifier {
modifier(base_config)
} else {
base_config
};
let span = span!(Level::INFO, "node", idx);
let _enter = span.enter();
let node = N::default();
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
.testing_node(exec.clone())
.with_types_and_provider::<N, BlockchainProvider<_>>()
.with_components(node.components_builder())
.with_add_ons(node.add_ons())
.launch_with_fn(|builder| {
let launcher = EngineNodeLauncher::new(
builder.task_executor().clone(),
builder.config().datadir(),
tree_config.clone(),
);
builder.launch_with(launcher)
})
.await?;
let mut node = NodeTestContext::new(node, self.attributes_generator).await?;
let genesis = node.block_hash(0);
node.update_forkchoice(genesis, genesis).await?;
let (prev, current) = nodes.split_at_mut(idx);
let current = current.first_mut().unwrap();
// Connect nodes if requested
if self.connect_nodes {
if let Some(previous_node) = nodes.last_mut() {
previous_node.connect(&mut node).await;
if let Some(prev_idx) = idx.checked_sub(1) {
prev[prev_idx].connect(current).await;
}
// Connect last node with the first if there are more than two
if idx + 1 == self.num_nodes &&
self.num_nodes > 2 &&
let Some(first_node) = nodes.first_mut()
let Some(first) = prev.first_mut()
{
node.connect(first_node).await;
current.connect(first).await;
}
}
nodes.push(node);
}
Ok((nodes, tasks, Wallet::default().with_chain_id(self.chain_spec.chain().into())))
@@ -196,8 +198,6 @@ where
+ Sync
+ Copy
+ 'static,
LocalPayloadAttributesBuilder<N::ChainSpec>:
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("E2ETestSetupBuilder")

View File

@@ -110,6 +110,7 @@ pub async fn setup_engine_with_chain_import(
// Create database path and static files path
let db_path = datadir.join("db");
let static_files_path = datadir.join("static_files");
let rocksdb_dir_path = datadir.join("rocksdb");
// Initialize the database using init_db (same as CLI import command)
// Use the same database arguments as the node will use
@@ -125,7 +126,8 @@ pub async fn setup_engine_with_chain_import(
db.clone(),
chain_spec.clone(),
reth_provider::providers::StaticFileProvider::read_write(static_files_path.clone())?,
);
reth_provider::providers::RocksDBProvider::builder(rocksdb_dir_path).build().unwrap(),
)?;
// Initialize genesis if needed
reth_db_common::init::init_genesis(&provider_factory)?;
@@ -311,6 +313,7 @@ mod tests {
std::fs::create_dir_all(&datadir).unwrap();
let db_path = datadir.join("db");
let static_files_path = datadir.join("static_files");
let rocksdb_dir_path = datadir.join("rocksdb");
// Import the chain
{
@@ -324,7 +327,11 @@ mod tests {
chain_spec.clone(),
reth_provider::providers::StaticFileProvider::read_write(static_files_path.clone())
.unwrap(),
);
reth_provider::providers::RocksDBProvider::builder(rocksdb_dir_path.clone())
.build()
.unwrap(),
)
.expect("failed to create provider factory");
// Initialize genesis
reth_db_common::init::init_genesis(&provider_factory).unwrap();
@@ -384,7 +391,11 @@ mod tests {
chain_spec.clone(),
reth_provider::providers::StaticFileProvider::read_only(static_files_path, false)
.unwrap(),
);
reth_provider::providers::RocksDBProvider::builder(rocksdb_dir_path)
.build()
.unwrap(),
)
.expect("failed to create provider factory");
let provider = provider_factory.database_provider_ro().unwrap();
@@ -470,12 +481,17 @@ mod tests {
// Create static files path
let static_files_path = datadir.join("static_files");
// Create rocksdb path
let rocksdb_dir_path = datadir.join("rocksdb");
// Create a provider factory
let provider_factory: ProviderFactory<MockNodeTypesWithDB> = ProviderFactory::new(
db.clone(),
chain_spec.clone(),
reth_provider::providers::StaticFileProvider::read_write(static_files_path).unwrap(),
);
reth_provider::providers::RocksDBProvider::builder(rocksdb_dir_path).build().unwrap(),
)
.expect("failed to create provider factory");
// Initialize genesis
reth_db_common::init::init_genesis(&provider_factory).unwrap();

View File

@@ -2,13 +2,12 @@
use crate::{
testsuite::actions::{Action, ActionBox},
NodeBuilderHelper, PayloadAttributesBuilder,
NodeBuilderHelper,
};
use alloy_primitives::B256;
use eyre::Result;
use jsonrpsee::http_client::HttpClient;
use reth_engine_local::LocalPayloadAttributesBuilder;
use reth_node_api::{EngineTypes, NodeTypes, PayloadTypes};
use reth_node_api::{EngineTypes, PayloadTypes};
use reth_payload_builder::PayloadId;
use std::{collections::HashMap, marker::PhantomData};
pub mod actions;
@@ -349,9 +348,6 @@ where
pub async fn run<N>(mut self) -> Result<()>
where
N: NodeBuilderHelper<Payload = I>,
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
>,
{
let mut setup = self.setup.take();

View File

@@ -1,15 +1,11 @@
//! Test setup utilities for configuring the initial state.
use crate::{
setup_engine_with_connection, testsuite::Environment, NodeBuilderHelper,
PayloadAttributesBuilder,
};
use crate::{setup_engine_with_connection, testsuite::Environment, NodeBuilderHelper};
use alloy_eips::BlockNumberOrTag;
use alloy_primitives::B256;
use alloy_rpc_types_engine::{ForkchoiceState, PayloadAttributes};
use eyre::{eyre, Result};
use reth_chainspec::ChainSpec;
use reth_engine_local::LocalPayloadAttributesBuilder;
use reth_ethereum_primitives::Block;
use reth_network_p2p::sync::{NetworkSyncUpdater, SyncState};
use reth_node_api::{EngineTypes, NodeTypes, PayloadTypes, TreeConfig};
@@ -138,28 +134,19 @@ where
) -> Result<()>
where
N: NodeBuilderHelper<Payload = I>,
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
>,
{
// Note: this future is quite large so we box it
Box::pin(self.apply_with_import_::<N>(env, rlp_path)).await
Box::pin(self.apply_with_import_(env, rlp_path)).await
}
/// Apply setup using pre-imported chain data from RLP file
async fn apply_with_import_<N>(
async fn apply_with_import_(
&mut self,
env: &mut Environment<I>,
rlp_path: &Path,
) -> Result<()>
where
N: NodeBuilderHelper<Payload = I>,
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
>,
{
) -> Result<()> {
// Create nodes with imported chain data
let import_result = self.create_nodes_with_import::<N>(rlp_path).await?;
let import_result = self.create_nodes_with_import(rlp_path).await?;
// Extract node clients
let mut node_clients = Vec::new();
@@ -186,9 +173,6 @@ where
pub async fn apply<N>(&mut self, env: &mut Environment<I>) -> Result<()>
where
N: NodeBuilderHelper<Payload = I>,
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
>,
{
// Note: this future is quite large so we box it
Box::pin(self.apply_::<N>(env)).await
@@ -198,9 +182,6 @@ where
async fn apply_<N>(&mut self, env: &mut Environment<I>) -> Result<()>
where
N: NodeBuilderHelper<Payload = I>,
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
>,
{
// If import_rlp_path is set, use apply_with_import instead
if let Some(rlp_path) = self.import_rlp_path.take() {
@@ -259,16 +240,10 @@ where
/// Note: Currently this only supports `EthereumNode` due to the import process
/// being Ethereum-specific. The generic parameter N is kept for consistency
/// with other methods but is not used.
async fn create_nodes_with_import<N>(
async fn create_nodes_with_import(
&self,
rlp_path: &Path,
) -> Result<crate::setup_import::ChainImportResult>
where
N: NodeBuilderHelper<Payload = I>,
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
>,
{
) -> Result<crate::setup_import::ChainImportResult> {
let chain_spec =
self.chain_spec.clone().ok_or_else(|| eyre!("Chain specification is required"))?;
@@ -301,9 +276,6 @@ where
+ use<N, I>
where
N: NodeBuilderHelper<Payload = I>,
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
>,
{
move |timestamp| {
let attributes = PayloadAttributes {

View File

@@ -278,7 +278,7 @@ where
let bundle_state_sorted = sort_bundle_state_for_comparison(re_executed_state);
let output_state_sorted = sort_bundle_state_for_comparison(original_state);
let filename = format!("{}.bundle_state.diff", block_prefix);
let diff_path = self.save_diff(filename, &bundle_state_sorted, &output_state_sorted)?;
let diff_path = self.save_diff(filename, &output_state_sorted, &bundle_state_sorted)?;
warn!(
target: "engine::invalid_block_hooks::witness",
@@ -308,13 +308,13 @@ where
if let Some((original_updates, original_root)) = trie_updates {
if re_executed_root != original_root {
let filename = format!("{}.state_root.diff", block_prefix);
let diff_path = self.save_diff(filename, &re_executed_root, &original_root)?;
let diff_path = self.save_diff(filename, &original_root, &re_executed_root)?;
warn!(target: "engine::invalid_block_hooks::witness", ?original_root, ?re_executed_root, diff_path = %diff_path.display(), "State root mismatch after re-execution");
}
if re_executed_root != block.state_root() {
let filename = format!("{}.header_state_root.diff", block_prefix);
let diff_path = self.save_diff(filename, &re_executed_root, &block.state_root())?;
let diff_path = self.save_diff(filename, &block.state_root(), &re_executed_root)?;
warn!(target: "engine::invalid_block_hooks::witness", header_state_root=?block.state_root(), ?re_executed_root, diff_path = %diff_path.display(), "Re-executed state root does not match block state root");
}

View File

@@ -15,6 +15,7 @@ reth-engine-primitives = { workspace = true, features = ["std"] }
reth-ethereum-engine-primitives.workspace = true
reth-payload-builder.workspace = true
reth-payload-primitives.workspace = true
reth-primitives-traits.workspace = true
reth-storage-api.workspace = true
reth-transaction-pool.workspace = true
@@ -43,4 +44,5 @@ op = [
"dep:op-alloy-rpc-types-engine",
"dep:reth-optimism-chainspec",
"reth-payload-primitives/op",
"reth-primitives-traits/op",
]

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