Compare commits

..

1074 Commits

Author SHA1 Message Date
Matthias Seitz
5c04d1abe1 fix: allow smaller header size 2025-12-16 17:08:53 +01: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
Matthias Seitz
84785f025e chore: bump v1.9.0 (#19507) 2025-11-05 13:33:46 +00:00
Matthias Seitz
5b062b21e1 chore: bump hardforks (#19506) 2025-11-05 12:30:37 +00:00
Cypher Pepe
1cd5b50aaf fix: dead link Sentry (#19505) 2025-11-05 11:54:27 +00:00
Matthias Seitz
89be06f6ad chore: bump version 1.8.4 (#19503) 2025-11-05 12:38:37 +01:00
Matthias Seitz
644ecce821 chore: bump min ckzg (#19504) 2025-11-05 11:10:16 +01:00
Matthias Seitz
b90badbe6d fix: skip code check in get_transaction_by_sender_and_nonce (#19502) 2025-11-05 09:49:17 +00:00
josé v
f4715ee62f chore: add custom hardforks example (#19391)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-04 23:50:41 +00:00
Avory
8ac37f3c67 docs(banlist): document timeout update behavior on re-ban (#19497) 2025-11-04 23:19:08 +00:00
Matthias Seitz
3ae73e63e5 perf: improve ethsendrawsync for op with flashblock (#19462)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-11-04 20:48:55 +00:00
theo
c3a60fa75a chore(op-reth/scr): update superchain-registry configs. Commit 9e3f71cee0e4e2acb4864cb00f5fbee3555d8e9f (#19495) 2025-11-04 18:39:34 +00:00
Matthias Seitz
fdcc540492 fix: spawn block fetching blocking (#19491)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-11-04 17:52:09 +00:00
Arsenii Kulikov
29761637ef fix: use cost when checking fee cap (#19493) 2025-11-04 18:17:22 +01:00
Alexey Shekhirin
5a6d3ddcad feat(reth-bench-compare): upstream from personal repo (#19488)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-04 15:10:05 +00:00
Brian Picciano
dd25caec12 chore: Various cleanups after consistent DB view removal (#19489) 2025-11-04 14:49:12 +00:00
sashass1315
583eb837f0 docs(trie): fix PrefixSetMut docs and freeze() comment (#19467) 2025-11-04 14:27:12 +00:00
Karl Yu
736a730a32 feat: support pending block tag in eth_getLogs for flashblocks (#19388)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-11-04 14:02:50 +00:00
Matthias Seitz
2cb4e1bd2a perf: use latest hash directly (#19486) 2025-11-04 13:30:42 +00:00
Matthias Seitz
6021a68dab perf(rpc): use cache for latest block and receipts (#19483) 2025-11-04 10:08:52 +00:00
Block Wizard
44e99e56f0 fix(net): remove capacity inflation from buffered blocks size calculation (#19481) 2025-11-04 09:05:27 +00:00
Matthias Seitz
f3cf8d5e10 feat: add helper to disable discovery (#19478) 2025-11-04 08:51:33 +00:00
Matthias Seitz
a311423292 chore: add queued reason to event (#19476) 2025-11-04 00:35:39 +00:00
Matthias Seitz
0c00c1b48a chore: add --miner.gaslimit alias (#19475) 2025-11-04 00:35:07 +00:00
Doryu
3f2432761b chore: Remove unused jsonrpsee tracing import in exex subscription example (#19448) 2025-11-03 21:40:55 +00:00
MIHAO PARK
bb694fb576 chore(grafana): deduce label by aggregate metrics (#18550) 2025-11-03 20:41:23 +00:00
joshieDo
c9897ad230 fix: update min_block on StaticFileProvider::update_index (#19469) 2025-11-03 19:53:10 +00:00
MIHAO PARK
66957c7902 chore(node): compact duration formatting in stage progress logs (#18720) 2025-11-03 19:47:35 +00:00
Matthias Seitz
24fa984da4 chore: add js-tracer feature to bins (#19441) 2025-11-03 19:25:50 +00:00
Matthias Seitz
93649fed0b chore: bump revm 31 (#19470)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-11-03 18:49:08 +00:00
Brian Picciano
846025545c fix(db): OverlayStateProviderFactory: default validation lower bound to 0 (#19468) 2025-11-03 17:14:45 +00:00
Matthias Seitz
a0eccf7128 chore: use name const for cli name (#19466) 2025-11-03 17:06:48 +00:00
Matthias Seitz
ea69063aae feat: schedule fusaka (#19455) 2025-11-03 16:51:46 +00:00
Alexey Shekhirin
7438bdbdf6 refactor(prune): derive EnumIter instead of explicit array of segments (#19465) 2025-11-03 15:28:14 +00:00
Matthias Seitz
7905fba953 feat: add broadcast channel for received flashblocks (#19459)
Co-authored-by: Federico Gimenez <fgimenez@users.noreply.github.com>
2025-11-03 12:55:36 +01:00
Matthias Seitz
1e8f35c046 feat(op-reth): add FlashblocksListeners container and receipt helpers (#19446)
Co-authored-by: Claude <noreply@anthropic.com>
2025-11-03 09:30:20 +00:00
sashass1315
714ebf749c fix: avoid unnecessary self.clone() in OpNetworkBuilder::network_config (#19451) 2025-11-03 08:52:49 +00:00
Matthias Seitz
1e27e73494 chore: add config_mut helpers (#19436) 2025-11-03 09:51:54 +01:00
github-actions[bot]
0bca7b150d chore(deps): weekly cargo update (#19443)
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-02 10:01:04 +00:00
Brian Picciano
780161a647 chore: OverlayStateProviderFactory: don't query for reverts unless necessary (#19412) 2025-11-01 15:13:51 +00:00
Matthias Seitz
e6aeba0d7d feat: support custom Download command defaults (#19437) 2025-11-01 10:51:46 +00:00
William Nwoke
dee0eca4d9 feat(tasks): distinguish blocking and non-blocking tasks in metrics (#18440)
Co-authored-by: Nathaniel Bajo <nathanielbajo@Nathaniels-MacBook-Pro.local>
Co-authored-by: Emilia Hane <emiliaha95@gmail.com>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-10-31 16:32:30 +00:00
Alexey Shekhirin
5f04690e28 revert: "feat: Add building and publishing of *.deb packages (#18615)" (#19011) 2025-10-31 16:30:47 +00:00
MIHAO PARK
71c124798c perf(cli): optimize StorageChangeSets import in merkle stage dump (#18022)
Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-10-31 16:08:07 +00:00
anim001k
dff382b8e2 fix: Inline value match in SparseTrie::find_leaf to remove redundant wrapper (#19138)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-10-31 16:06:06 +00:00
FT
d8729a9d2c chore(docker): remove apt-get upgrade to ensure reproducible and faster builds (#19080) 2025-10-31 16:04:54 +00:00
oooLowNeoNooo
a5eb01b26b fix: rename variable in block_hash method from 'code' to 'hash' (#19269) 2025-10-31 16:00:06 +00:00
Alexey Shekhirin
a43345b54c perf(tree): only chunk multiproof targets if needed (#19326) 2025-10-31 15:45:03 +00:00
Ragnar
e894db8e07 perf: optimize SyncHeight event handling to avoid recursive calls (#19372)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-31 15:44:14 +00:00
bigbear
b05eb5f793 fix(txpool): correct propagate field name in Debug output (#19278) 2025-10-31 15:36:22 +00:00
Micke
1c5c709d61 chore(codecs): replace todo with unimplemented in Compact derive (#19284) 2025-10-31 15:32:28 +00:00
Avory
3bb90e64a2 fix(beacon-api-sidecar): use correct block metadata for reorged blobs (#19424) 2025-10-31 15:08:45 +00:00
Matthias Seitz
ecd49aed11 perf: only chunk if more > 1 available (#19427) 2025-10-31 15:03:10 +00:00
Udoagwa Franklin
af9b04c1a3 feat(op-reth): implement miner_setGasLimit RPC (#19247)
Co-authored-by: frankudoags <frankudoags.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-31 15:02:51 +00:00
Alexey Shekhirin
1f2f1d432f feat(node): CLI argument for sync state idle when backfill is idle (#19429) 2025-10-31 14:16:27 +00:00
Matthias Seitz
9f4f66dd8e perf: bias towards proof results (#19426) 2025-10-31 12:48:33 +00:00
Brian Picciano
728e03706c feat(reth-bench): Default --wait-time to 250ms (#19425) 2025-10-31 12:39:40 +00:00
Gengar
b6be053cbe fix(codecs): return remaining slice in EIP-1559 from_compact (#19413) 2025-10-31 11:55:19 +00:00
Matthias Seitz
8a72b519b2 chore: add count field to trace (#19422) 2025-10-31 11:53:01 +00:00
Brian Picciano
4d437c43bf fix: Properly set MerkleChangeSets checkpoint in stage's fast-path (#19421) 2025-10-31 10:56:37 +00:00
Ragnar
60fba66a4f fix(compact): prevent bitflag overflow by using usize accumulator (#19408) 2025-10-31 09:32:51 +00:00
Eric Woolsey
d29370ebf8 chore: update superchain reg to c9881d543174ff00b8f3a9ad3f31bf4630b9743b (#19418) 2025-10-31 09:09:29 +00:00
Yash
cff942ed0e chore: add tracing features to node-core crate (#19415)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-31 00:01:32 +00:00
Forostovec
6fafff5f14 fix: highest_nonces update in PendingPool::remove_transaction (#19301)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-30 20:43:11 +00:00
Wojtek Łopata
dc8efbf9b3 feat: add --rpc.evm-memory-limit flag (#19279)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-30 19:53:43 +00:00
Skylar Ray
fccf76a19a fix(engine): remove redundant parent_to_child cleanup in insert_executed (#19380) 2025-10-30 18:51:27 +00:00
Lancelot de Ferrière
59bf11779c feat: Output the block execution outputs after validating (reth-stateless) (#19360) 2025-10-30 15:50:29 +00:00
sashass1315
e9400527cd chore(net): avoid cloning GetBlockBodies request (#19404) 2025-10-30 15:12:10 +00:00
leniram159
5f5dbb0121 fix: accurate build features reporting in reth --version (#19124) 2025-10-30 14:48:30 +00:00
Brian Picciano
d87d0d1a1f fix: Prune checkpoint fixes (#19407) 2025-10-30 13:40:18 +00:00
Federico Gimenez
be291144ee fix(engine): trigger live sync after backfill completes at finalized (#19390) 2025-10-30 12:55:32 +00:00
Emilia Hane
7c007f7cda fix(cli): Metrics log when passed metrics port 0 (#19406)
Co-authored-by: Varun Doshi <doshivarun202@gmail.com>
2025-10-30 11:50:19 +00:00
Matthias Seitz
bec4d7c436 perf: box ForkId in Peer struct to reduce size (#19402) 2025-10-29 23:50:39 +00:00
Matthias Seitz
3fa10defd1 chore: bump discv5 (#19400) 2025-10-29 22:06:03 +00:00
Forostovec
f303b28974 chore: reuse gzip read buffer to avoid per-iteration allocation (#19398) 2025-10-29 22:02:30 +00:00
strmfos
b15c285310 perf(codecs): avoid String allocation in proc macro type checking (#19354) 2025-10-29 21:11:13 +00:00
Matthias Seitz
752891b7cb chore: fix unused warning (#19395) 2025-10-29 22:19:42 +01:00
Matthias Seitz
e808b9ab8f chore: fix unused dep (#19397) 2025-10-29 22:19:29 +01:00
leniram159
be50b284b3 feat: display blob params alongside hardfork info (#19358) 2025-10-29 20:34:31 +00:00
Galoretka
1ed41d5151 chore(primitives-traits): gate test-only modules (#19393) 2025-10-29 20:24:40 +00:00
theo
7dc07e8258 feat(jovian/rpc): update receipts to transmit over RPC with Jovian compatible fields (#19368)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-29 20:22:54 +00:00
Dan Cline
7989c7094b docs: fix otlp flag in monioring docs (#19394) 2025-10-29 20:09:43 +00:00
Avory
715369b819 docs: improve RESS protocol module documentation (#19370) 2025-10-29 16:36:02 +00:00
Merkel Tranjes
ea2b26f46a fix: remove PersistenceState from TreeCtx (#19356) 2025-10-29 16:32:43 +00:00
phrwlk
dbc93466ca fix(engine): align compute_trie_input docs with actual persistence behavior (#19385)
Co-authored-by: Brian Picciano <me@mediocregopher.com>
2025-10-29 15:55:35 +00:00
theo
1114a9c07e feat(precompiles/jovian): add jovian precompiles to op-reth (#19333)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-29 17:02:57 +01:00
Mablr
66cfa9ed1a feat(rpc): implement debug_dbGet (#19369) 2025-10-29 15:45:58 +00:00
Jennifer
30942597db fix: add more context to expected hive failures (#19363)
Co-authored-by: rakita <rakita@users.noreply.github.com>
2025-10-29 15:31:35 +00:00
theo
8a795e7d40 feat(jovian/timestamps): add jovian timestamps to op-reth (#19290) 2025-10-29 16:28:17 +01:00
Brian Picciano
d5a7ecf45a chore: Update nix flake (#19386) 2025-10-29 14:39:03 +00:00
emiliano-conduitxyz
5a4287aa6d fix(op-reth): use latest for runtime image (#19331) 2025-10-29 14:35:42 +01:00
Brian Picciano
6659080dc0 fix: Don't always clone in-memory overlays in OverlayStateProviderFactory (#19383) 2025-10-29 13:18:26 +00:00
Matthias Seitz
caaedfadcb chore: bump 1.8.3 (#19379) 2025-10-29 13:07:30 +01:00
joshieDo
644be05659 feat: add pruning of transactions from static-files (#19241) 2025-10-29 11:50:51 +00:00
Brian Picciano
527c24df6d fix(trie): use block hash in OverlayStateProviderFactory (#19353) 2025-10-29 10:34:51 +00:00
Đạt Nguyễn
17a984929b feat: impl a function to create new instance of TransactionEvents (#19375)
Co-authored-by: Neo Krypt <neo@canxium.org>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-29 10:00:37 +00:00
Karl Yu
3827e5cb1d perf: wrap tx with Arc to avoid deep cloning (#19350) 2025-10-29 08:30:29 +00:00
YK
10d9a7e3c6 refactor(trie): restructure proof task workers into structs (#19344) 2025-10-29 08:09:39 +00:00
theo
77ef028aca fix(op-reth/consensus): fixes header validation for jovian. decouple excess blob gas and blob gas used (#19338) 2025-10-29 07:39:29 +00:00
Matthias Seitz
ff46daddb6 feat: insert at timestamp (#19365) 2025-10-28 20:29:26 +00:00
Mablr
adb4f48471 feat(reth-optimism-node): Add OP E2E mineblock test with isthmus activated at genesis (#19305) 2025-10-28 20:13:44 +00:00
Matthias Seitz
6651ae7852 chore: add ChainHardforks::extend (#19332) 2025-10-28 20:36:23 +01:00
joshieDo
ac4f80ded3 chore: dont write receipts to both storages on archive node (#19361) 2025-10-28 17:22:20 +00:00
Matthias Seitz
3ce6e87ab9 chore: update docs for expected test failure (#19343) 2025-10-28 16:07:39 +00:00
joshieDo
020eb6ad7e fix(pipeline): ensure we dont pass an outdated target to header stage (#19351) 2025-10-28 15:02:19 +00:00
YK
5091482dec refactor(trie): reorder proof_task.rs for better code organization (#19342) 2025-10-28 11:14:08 +00:00
Avory
0f3e0eee63 refactor: make DatabaseProof trait stateful (#18753) 2025-10-28 10:14:07 +00:00
Francis Li
5e2ed163f3 fix(engine): Eliminates spurious warning logs in prewarm task (#19133)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-28 09:27:33 +00:00
dependabot[bot]
0da38b9732 chore(deps): bump actions/download-artifact from 5 to 6 (#19336)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-28 09:23:53 +00:00
dependabot[bot]
e547c027f3 chore(deps): bump actions/upload-artifact from 4 to 5 (#19335)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-10-28 09:22:33 +00:00
YK
7e6f676d16 feat(metrics): improve multiproof worker metrics (#19337) 2025-10-28 09:00:22 +00:00
YK
e2b5c7367c chore: update Grafana dashboard with split pending multiproof metrics (#19339) 2025-10-28 06:44:19 +00:00
josé v
50e88c29be chore: replace CacheDB with State<DB> in RPC crate (#19330)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-10-27 22:00:58 +00:00
Jennifer
1581aaa615 fix: update section name in expected failures, add more concise comments (#19328) 2025-10-27 21:46:29 +00:00
Brian Picciano
ffeaa4772d chore(engine): Remove ConsistentDbView (#19188)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-10-27 19:09:21 +00:00
Mablr
a264ccbbc2 feat(metrics): add push gateway support for Prometheus metrics (#19243) 2025-10-27 17:11:23 +00:00
Dan Cline
b1dfbc7e88 chore: remove trie capacity metrics (#19327) 2025-10-27 17:07:37 +00:00
Gengar
0569e884c4 docs: improve documentation for mock database and transactions (#19302) 2025-10-27 15:59:48 +00:00
theo
f9c89a9bc9 feat(jovian/block-validation): fix block validation for jovian (#19304) 2025-10-27 15:59:04 +00:00
Brian Picciano
7e59141c4b fix(trie): Rewrite InMemoryTrieOverlay (with proptests!) (#19277) 2025-10-27 15:18:48 +00:00
Alexey Shekhirin
080cf72464 chore(trie): reduce sparse trie tracing (#19321) 2025-10-27 15:16:56 +00:00
phrwlk
a6fe713a6c chore: remove dead OpL1BlockInfo.number field and writes (#19325) 2025-10-27 14:42:55 +00:00
YK
eed0d9686c refactor(trie): Unify proof return types (#19311) 2025-10-27 13:58:28 +00:00
Brian Picciano
fa1f86cb91 fix(prune): Add unused variants back to PruneSegment enum (#19318) 2025-10-27 13:12:22 +00:00
radik878
f088ec09cb docs(eth-wire): update docs to reflect eth-wire-types, alloy_rlp, version-aware decoding, and RLPx multiplexing (#19319) 2025-10-27 11:58:55 +00:00
Matthias Seitz
106ffefc0f chore: use hex bytes type (#19317) 2025-10-27 12:57:44 +01:00
Gengar
bb73d794fd docs: populate modify-node section with node-custom-rpc implementation guide (#18672) 2025-10-27 12:57:27 +01:00
sashass1315
19f5d51d86 chore: remove redundant PhantomData from NodeHooks (#19316) 2025-10-27 11:41:48 +00:00
Brian Picciano
be73e4a246 fix(trie): Fix trie_reverts not returning sorted nodes (#19280) 2025-10-27 10:48:17 +00:00
Alexey Shekhirin
6b3534d407 ci: pin Bun to v1.2.23 (#19315) 2025-10-27 10:32:37 +00:00
Alexey Shekhirin
763bf350be chore(net): upgrade some noisy spans to TRACE (#19312) 2025-10-27 10:02:52 +00:00
VolodymyrBg
094594142f fix(engine): module doc to reflect schnellru::LruMap backend (#19296) 2025-10-27 11:18:10 +01:00
Maximilian Hubert
4f660dac85 fix(fs): correct ReadLink error message and add missing read_link wra… (#19287) 2025-10-27 11:17:29 +01:00
Galoretka
74cc561917 chore(ethereum): remove redundant std::default::Default import (#19299) 2025-10-27 11:16:16 +01:00
guha-rahul
ded9d3ce33 refactor: add more Snap response types (#19303)
Co-authored-by: suhas-sensei <suhas.ghosal2002@gmail.com>
2025-10-27 10:49:39 +01:00
YK
53119fd5a1 refactor(trie): rename queue_storage_proof to send_storage_proof (#19310) 2025-10-27 10:49:04 +01:00
github-actions[bot]
159ff01cd2 chore(deps): weekly cargo update (#19300)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
2025-10-26 16:37:00 +00:00
phrwlk
e22a511764 fix(node): classify connect_async failures as WebSocket and use Url parse error (#19286) 2025-10-25 06:37:22 +00:00
Alexey Shekhirin
0c8417288b feat(tracing): set default OTLP log level to WARN (#19283) 2025-10-24 18:39:08 +00:00
Alexey Shekhirin
25f0d896d9 chore(trie): do not create a parent span for proof worker handle (#19281) 2025-10-24 15:57:51 +00:00
AJStonewee
f177103937 fix(trie): correct comment in sparse_trie_reveal_node_1 test (#19193) 2025-10-24 15:13:12 +00:00
Alexey Shekhirin
dc781126c2 feat(trie): proof task tracing improvements (#19276) 2025-10-24 14:04:01 +00:00
Jennifer
a69bbb3d7b fix: hive tests consume test suite (#19240)
Co-authored-by: Federico Gimenez <federico.gimenez@gmail.com>
2025-10-24 13:50:53 +00:00
YK
f29f4caf0e perf: Eliminate spawn_blocking in multiproof manager (#19203) 2025-10-24 11:56:57 +00:00
Galoretka
b88b46ac1f fix(optimism): guard follow-up inserts by payload_id to prevent mixed sequences (#19264) 2025-10-24 11:48:29 +00:00
0xeabz
a767fe3b14 feat: allow using SafeNoSync for MDBX (#18945)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-10-24 11:25:14 +00:00
Dan Cline
4a24cb3b49 fix(engine): re-insert storage cache and use arc (#18879) 2025-10-24 09:32:55 +00:00
josé v
ddcfc8a440 chore: add add_or_replace_if_module_configured method (#19266) 2025-10-24 09:31:22 +00:00
strmfos
51fbd5a519 fix: no_std compatibility in reth-optimism-chainspec (#19271) 2025-10-24 08:36:30 +00:00
Yash
08fc0a918d feat: eth_fillTransaction (#19199)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
Co-authored-by: jxom <7336481+jxom@users.noreply.github.com>
2025-10-24 08:16:21 +00:00
radik878
189b00b1e6 chore(net): remove unnecessary TODO (#19268) 2025-10-23 20:03:16 +00:00
joshieDo
5a9c7703d1 chore: rm StaticFileReceipts pruner (#19265) 2025-10-23 18:44:06 +00:00
joshieDo
3883df3e6c chore: remove db pruning of header/txs segments (#19260) 2025-10-23 17:20:55 +00:00
Arsenii Kulikov
4adfa286f7 fix: return hashed peer key as id (#19245) 2025-10-23 16:17:26 +00:00
Léa Narzis
6739914ce7 feat(otlp-tracing): enable to export traces with grpc export with tracing-otlp and tracing-otlp-protocol arg (#18985) 2025-10-23 14:44:24 +00:00
Dan Cline
7b7f563987 fix(engine): shrink tries after clearing (#19159) 2025-10-23 14:38:32 +00:00
Alexey Shekhirin
81b1949c3c fix(cli): prune CLI argument names (#19215) 2025-10-23 16:06:04 +02:00
Ragnar
f3b9349d6f docs: add usage examples and documentation to NoopConsensus (#19194) 2025-10-23 13:34:51 +00:00
Alexey Shekhirin
3d3a05386a refactor(static-file): remove unused segments (#19209) 2025-10-23 13:31:15 +00:00
Fallengirl
75931f8772 chore: align env filter comment with configured directives (#19237) 2025-10-23 13:13:03 +00:00
Matthias Seitz
c54719145b fix: use known paris activation blocks in genesis parsing (#19258) 2025-10-23 12:43:56 +00:00
Roman Hodulák
71f91cf4eb feat(prune): Add an empty reth-prune-db crate (#19232) 2025-10-23 11:43:24 +00:00
Matthias Seitz
ce876a96ad fix: use network id in p2p command (#19252) 2025-10-23 13:39:12 +02:00
Fallengirl
b2236d1db7 docs: correct Payment tx type from 0x7E to 0x2A (#19255) 2025-10-23 13:20:59 +02:00
YK
4548209e7b perf: rm pending queue from MultiproofManager (#19178) 2025-10-23 07:19:21 +00:00
Alexey Shekhirin
f8845c6fbb fix(engine): payload processor tracing event targets (#19223) 2025-10-23 04:36:16 +00:00
theo
bcef01ce47 feat(jovian): track da footprint block limit. Update basefee calculation (#19048)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-10-22 23:28:23 +00:00
Matthias Seitz
346ef408a4 chore: swap order for canon stream (#19242) 2025-10-22 20:38:53 +00:00
radik878
4f6cc7a359 fix(node): remove unused ConsensusLayerHealthEvent variants (#19238) 2025-10-22 18:20:25 +00:00
Dan Cline
1972ec0949 revert: "fix(engine): flatten storage cache (#18880)" (#19235) 2025-10-22 16:33:54 +00:00
Federico Gimenez
8119045258 chore(e2e): relax bounds (#19231) 2025-10-22 16:29:55 +00:00
Alexey Shekhirin
fa2f173aac chore(storage): remove UnifiedStorageWriterError (#19210) 2025-10-22 16:10:33 +00:00
Jennifer
bab9dee555 fix: rename consume-* test suite (#19230) 2025-10-22 15:16:29 +00:00
Roman Hodulák
df0da36bc4 test(hive): Ignore new failures that are won't fix (#19218) 2025-10-22 15:04:08 +00:00
Matthias Seitz
f438a6cc83 chore: add elapsed info log (#19211) 2025-10-22 15:02:37 +00:00
wizard
b9f6068f59 fix: incorrect RPC namespace reference (#19225) 2025-10-22 14:04:10 +00:00
Matthias Seitz
778146cb01 chore: use retrylayer for benchmarkcontext (#19227) 2025-10-22 13:36:49 +00:00
Brian Picciano
47dc43287f fix(reth-bench): Lower block channel capacity and make it configurable (#19226) 2025-10-22 13:27:03 +00:00
Brian Picciano
712569d4ce feat: warning log when blocked on execution cache (#19222) 2025-10-22 13:04:16 +00:00
Brian Picciano
35b28ea543 fix: OverlayStateProviderFactory: validating trie changeset range and revert target (#19207) 2025-10-22 12:30:26 +00:00
greg
7a98145def fix: captured impl trait lifetime (#19216)
Signed-off-by: Gregory Edison <gregory.edison1993@gmail.com>
2025-10-22 11:58:01 +00:00
Matthias Seitz
56d8cea939 chore: only alloc required capacity (#19217) 2025-10-22 11:40:56 +00:00
Matthias Seitz
bb620736b9 perf: check prewarm termination multiple times (#19214) 2025-10-22 11:29:00 +00:00
Arsenii Kulikov
0ea75f5edf fix: small features fix (#19212) 2025-10-22 11:21:59 +00:00
Alexey Shekhirin
b5df3f31b2 refactor(prune): remove receipts log filter segment (#19184) 2025-10-22 10:22:11 +00:00
robinsdan
4d3c163120 perf(net): convert Bytes to BytesMut to avoid reallocation (#19204) 2025-10-22 08:46:26 +00:00
Alexey Shekhirin
ada053aa67 chore: remove rkrasiuk from codeowners (#19206) 2025-10-22 10:10:47 +02:00
YK
60e3eded5e refactor: decouple max proof task concurrency from inflight proof limits (#19171) 2025-10-22 06:53:55 +00:00
Dan Cline
e810df943b feat(engine): improve payload validator tracing spans 2 (#19155) 2025-10-21 22:14:16 +00:00
Avory
c6af584b00 docs: improve SealedBlockRecoveryError documentation (#19120) 2025-10-21 19:19:56 +00:00
Merkel Tranjes
1d58ae1ff8 feat: improve oversized data error message (#19190) 2025-10-21 19:15:13 +00:00
Dmitry
ba6d593aa0 chore: fix misleading log message for body size check (#19173) 2025-10-21 19:13:25 +00:00
Alex Pikme
876346d143 fix: add arrayvec to dev-dependencies in reth-trie-common (#19192) 2025-10-21 19:12:57 +00:00
Matthias Seitz
21785a30e8 test: add node record parse test (#19172) 2025-10-21 21:20:09 +02:00
Matthias Seitz
2c086f0ed3 chore: rm generic array dep from discv4 (#19140) 2025-10-21 21:18:45 +02:00
0xsensei
563ae0d30b fix: drop support for total difficulty table (#16660)
Co-authored-by: Aditya Pandey <adityapandey@Adityas-MacBook-Air.local>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
Co-authored-by: joshieDo <93316087+joshieDo@users.noreply.github.com>
2025-10-21 16:55:08 +00:00
maradini77
645672916a fix: remove unnecessary trait bounds in extend_sorted_vec helper (#19154) 2025-10-21 15:53:08 +00:00
Federico Gimenez
01820fdaf7 feat(e2e): add builder API for configuring test node setups (#19146) 2025-10-21 13:04:19 +00:00
Brawn
93b63bc765 chore: fix incorrect hex value in comment (0x2A instead of 0x7E) (#19181) 2025-10-21 12:45:37 +00:00
David Klank
dbceffdcf4 refactor(ipc): simplify RpcServiceCfg from enum to struct (#19180) 2025-10-21 12:37:58 +00:00
Arsenii Kulikov
936baf1232 refactor: remove FullNodePrimitives (#19176) 2025-10-21 12:05:38 +00:00
Alexey Shekhirin
7263a7b4eb fix(cli): prune config saving to file (#19174) 2025-10-21 11:59:11 +00:00
joshieDo
e21048314c chore: remove total difficulty from HeaderProvider (#19151) 2025-10-21 10:56:36 +00:00
MozirDmitriy
f0c0b3db4e feat(storage): replace unreachable todo!() with explicit unreachable!() in compact derive (#19152) 2025-10-21 10:21:36 +00:00
Alex Pikme
792b82d895 perf: fix redundant Arc clone in file_client tests (#19170) 2025-10-21 10:48:46 +02:00
Dan Cline
49bbcdc38c chore: rm high frequency otel-related debug logs (#19147) 2025-10-20 17:09:57 +00:00
Brian Picciano
915b627f4f fix: Revert "feat(engine): improve payload validator tracing spans (#18960)" (#19145) 2025-10-20 16:06:23 +00:00
malik
e198a38d62 perf: batch byte for serialization (#19096)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-20 15:04:31 +00:00
0xMushow
be2306da31 chore(config): clean up gas limit code (#19144) 2025-10-20 14:41:10 +00:00
Skylar Ray
90e0d37367 fix: remove tautological assertions in validator tests (#19134) 2025-10-20 14:19:55 +00:00
Brian Picciano
6ee53922d0 fix(prune): Disable pruning limits (#19141) 2025-10-20 14:05:16 +00:00
Arsenii Kulikov
20f807778d Revert "refactor: unify Pipeline creation codepaths" (#19143) 2025-10-20 13:15:10 +00:00
Brian Picciano
ca19c19b38 chore: fix+update nix flake (#19142) 2025-10-20 13:04:05 +00:00
Brian Picciano
8eb5461dad chore(trie): Add lifetime to cursors returned from Trie/HashedCursorFactorys (#19114) 2025-10-20 12:18:24 +00:00
Andrew Huang
79c11ff567 fix: Add support for init-state for op-reth chains that are not op-mainnet… (#19116)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-20 14:06:15 +02:00
Arsenii Kulikov
c5a52c7d44 fix(e2e): gracefully wait for payload (#19137) 2025-10-20 10:50:51 +00:00
Matthias Seitz
11c449feb0 feat: add helper apply fns (#19122) 2025-10-20 09:29:09 +00:00
leopardracer
e185025447 fix: Remove duplicate debug log in write_blocks_to_rlp (#19132) 2025-10-20 10:57:03 +02:00
github-actions[bot]
2f9281b6c1 chore(deps): weekly cargo update (#19126)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-19 18:44:55 +02:00
Micke
67bf37babd chore: remove redundant collect in debug trace (#19121) 2025-10-18 17:17:43 +02:00
GarmashAlex
10ed1844e4 fix(net): correct error messages for decrypt and header paths (#19039) 2025-10-18 17:11:15 +02:00
Matthias Seitz
a718752bf5 chore: fix clippy (#19118) 2025-10-18 11:22:02 +02:00
Dmitry
a8ef47d14c docs: fix wrong label for --color=auto (#19110)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-18 11:21:10 +02:00
Ignacio Hagopian
46228d0a18 feat(stateless): make UncompressedPublicKey serializable (#19115)
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
2025-10-18 10:41:56 +02:00
GarmashAlex
8d91b9e443 feat(cli): Reuse a single StaticFileProducer across file import chunks (#18964)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-18 10:34:29 +02:00
Dan Cline
63f560705c feat: add capacity metrics for tries (#19117) 2025-10-18 09:56:56 +02:00
Alexey Shekhirin
4a32bc0fe5 feat(engine): improve payload validator tracing spans (#18960)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
2025-10-17 21:20:12 +00:00
Arsenii Kulikov
a5618f57a8 feat: convert pooled blobs transition (#19095) 2025-10-17 17:34:38 +00:00
leopardracer
6a918f4cab fix: Deduplicate hashed storage preparation in MemoryOverlayStateProvider (#19087) 2025-10-17 15:48:22 +00:00
YK
d1f6637a5a refactor: naming fix for multiproof dispatch (#19102) 2025-10-17 15:46:17 +00:00
Dharm Singh
1b830e9ed1 feat: derive dev accounts from mnemonic in dev mode (#18299)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-10-17 15:19:21 +00:00
Matthias Seitz
928d91dbf9 chore: add comment section for claude (#19108) 2025-10-17 14:45:24 +00:00
crazykissshout
1634535e00 fix: add bundle and transaction context to call_many errors (#18127)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-17 14:40:26 +00:00
Arsenii Kulikov
ca26219aa6 feat: convert blobs at RPC (#19084) 2025-10-17 11:45:23 +00:00
sashass1315
cfb26912d3 fix(cli): remove redundant EthChainSpec bound in run_with_components (#19106) 2025-10-17 10:59:49 +00:00
maradini77
e46a9bc40c fix(sim): clamp bundle timeout to max instead of falling back to default (#18840)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-17 10:47:14 +00:00
futreall
4c7b1ed9d4 fix: add revm-state to dev-dependencies of chain-state crate (#19044) 2025-10-17 10:11:11 +00:00
Dan Cline
ff68bfe935 chore: lower ecies instrument calls to trace (#19004) 2025-10-17 10:05:18 +00:00
Matthias Seitz
a2c50947b8 chore: exhaustive match for builtin tracer (#19105) 2025-10-17 09:42:18 +00:00
Léa Narzis
3af2c93fc6 feat(cli): add method CliRunner::block_on (#19088) 2025-10-17 09:24:19 +00:00
Roman Hodulák
73af300286 fix(cli): Remove duplicit static file header and transaction append (#19103) 2025-10-17 08:45:00 +00:00
Matthias Seitz
a8e387bd10 chore: init state touchups (#19066) 2025-10-16 21:07:40 +00:00
stevencartavia
48d8298e1f feat: add Pool::remove_transaction(hash) (#19098) 2025-10-16 21:02:26 +00:00
Avory
53ef7a386c docs: fix duplicate method comments in ChainInfoTracker (#18929) 2025-10-16 20:53:36 +00:00
AJStonewee
25e8d6bb77 chore: clarify the wrong Length description (#19094) 2025-10-16 20:06:08 +00:00
Matthias Seitz
cc490b668a fix: accurately track account and code weighs (#19091) 2025-10-16 15:44:08 +00:00
Matthias Seitz
5887a15966 revert: "fix: Revert "chore: disable fee charge in env"" (#19073) 2025-10-16 14:30:36 +00:00
Matthias Seitz
ff2236e5b4 fix: support rlp hex in read_header_from_file (#19089) 2025-10-16 14:25:56 +00:00
MozirDmitriy
8788782f25 fix(net): remove redundant remove of evicted hash in fetcher (#19083) 2025-10-16 12:40:12 +00:00
Karl Yu
7e006d6845 chore: remove unused rayon pool from WorkloadExecutor (#19065)
Co-authored-by: sashass1315 <sashass1315@gmail.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-16 12:06:05 +00:00
Alexey Shekhirin
e969262c7e refactor: rename disable_caching_and_prewarming to disable_prewarming (#19072) 2025-10-16 10:58:42 +00:00
kevaundray
be648d950c feat: Stateless validation function receives public keys corresponding to each transaction (#17841)
Co-authored-by: Wolfgang Welz <welzwo@gmail.com>
2025-10-16 10:21:15 +00:00
Skylar Ray
5beeaedfae chore(fs-util): remove redundant tmp_path clone (#19003) 2025-10-16 10:10:11 +00:00
Mablr
386eaa3ff6 fix(discv5): get fork_id from Enr for all network stacks (#18988)
Co-authored-by: emhane <elsaemiliaevahane@gmail.com>
Co-authored-by: Emilia Hane <emiliaha95@gmail.com>
2025-10-16 09:56:27 +00:00
Brian Picciano
be94d0d393 feat(trie): Merge trie changesets changes into main (#19068)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
Co-authored-by: Roman Hodulák <roman.hodulak@polyglot-software.com>
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
2025-10-16 09:52:35 +00:00
GarmashAlex
3de82cf2bd fix(net): Increment out_of_order_requests in BodiesDownloader on range reset (#19063) 2025-10-16 08:58:05 +00:00
Alexey Shekhirin
84aa51481b chore: rename CLI argument --tracing-otlp-level to --tracing-otlp.filter (#19061)
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-16 08:42:25 +00:00
YK
a84bef0832 refactor: revert Remove max_proof_task_concurrency as configurable variable" (#19062) 2025-10-16 07:28:04 +00:00
Copilot
926b1a43fe refactor: Remove max_proof_task_concurrency as configurable variable (#19009)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yongkangc <46377366+yongkangc@users.noreply.github.com>
Co-authored-by: Yong Kang <chiayongkang@hotmail.com>
2025-10-15 23:52:27 +00:00
Matthias Seitz
a096709865 chore: defense for new SubscriptionKind item (#19054) 2025-10-15 21:28:29 +00:00
James Prestwich
65a7f35a56 feat: use env filter for otlp, respect otel env var (#19050) 2025-10-15 21:10:24 +00:00
drhgencer
5c19ce7580 refactor(txpool): reuse cached gas_limit value (#19052) 2025-10-15 20:19:03 +00:00
Arsenii Kulikov
f6a583ffc4 feat: stricter bound (#19049) 2025-10-15 20:15:47 +00:00
radik878
080d508ebf fix(session): remove Clone derive from SessionCounter (#19051) 2025-10-15 20:14:42 +00:00
Arsenii Kulikov
7779ed8c73 feat: bump revm (#18999) 2025-10-15 17:55:35 +00:00
sashass1315
fd4597e9bd chore(primitives-traits): relax SignerRecoverable bounds for Extended<B,T> (#19045) 2025-10-15 17:53:47 +00:00
Matthias Seitz
6bb0d1b929 chore: increase versioned hash index cache (#19038) 2025-10-15 17:52:03 +00:00
Arsenii Kulikov
20b14d59c7 fix: correct Compact impl for Option (#19042) 2025-10-15 16:08:26 +00:00
Matthias Seitz
8880119e17 fix: use header type generic for mask (#19037) 2025-10-15 14:27:44 +00:00
Federico Gimenez
daa91939f8 chore(ci): update expected failures (#19034) 2025-10-15 13:43:12 +00:00
YK
eb9b08c696 perf: background init of workers (#19012) 2025-10-15 13:16:54 +00:00
Galoretka
0cbd514e4b feat(engine): deprecate TestPipelineBuilder::with_executor_results (#19017) 2025-10-15 13:03:49 +00:00
Matthias Seitz
63ec808973 fix: respect cli blob size setting (#19024) 2025-10-15 14:52:43 +02:00
Matthias Seitz
1b952def26 fix: unused warnings for tracing (#19025) 2025-10-15 14:39:54 +02:00
Matthias Seitz
39ef9dd528 chore: bump alloy-core (#19026) 2025-10-15 14:39:06 +02:00
Luca Provini
fc03347cdd feat: add pending sequence as pub (#19022) 2025-10-15 14:02:26 +02:00
Skylar Ray
45194fc5df chore: remove unused Args struct from exex-subscription example (#19019) 2025-10-15 11:22:21 +00:00
Udoagwa Franklin
6b08d30e14 feat(devp2p): make eth p2p networkId configurable (#19020)
Co-authored-by: frankudoags <frankudoags.com>
2025-10-15 11:15:42 +00:00
Federico Gimenez
7fc3980904 chore(ci): bump hive eest to v5.3.0 (#19021) 2025-10-15 10:45:36 +00:00
josé v
731e107ee6 chore: refactor loop in add_new_transactions (#19006) 2025-10-15 09:42:11 +00:00
GarmashAlex
a1aed9d9f0 chore(privitives-traits): remove unused serde derives and camelCase attribute (#19014) 2025-10-15 09:41:02 +00:00
Ivan Wang
ee6cac72de feat: add metrics for safe and finalized block heights (#18987) 2025-10-15 09:07:42 +00:00
Matthias Seitz
2f82b7c771 chore: bump book timeout (#19016) 2025-10-15 11:06:34 +02:00
Jennifer
b6f7fae19a ci: Add tests for Paris scenario in hive.yml (#19013) 2025-10-15 08:49:51 +00:00
Brian Picciano
00f173307c fix: Set Era pipeline stage to last checkpoint when there is no target (#19000) 2025-10-15 08:15:34 +00:00
stevencartavia
06b33fd64b chore: replace poll_next_unpin loop with poll_recv_many (#18978) 2025-10-15 08:12:01 +00:00
Karl Yu
856ad08776 refactor(engine): simplify InvalidBlockWitnessHook::on_invalid_block for better testability (#18696) 2025-10-15 03:50:41 +00:00
Julian Meyer
092599bd2c fix: required optimism primitives features in db-api (#19005) 2025-10-15 03:38:21 +00:00
YK
11c9949add refactor(trie): remove proof task manager (#18934)
Co-authored-by: Brian Picciano <me@mediocregopher.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-10-15 01:49:39 +00:00
anim001k
082b5dad37 refactor(storage): fix ChainStateKey enum variant name (#18992) 2025-10-15 01:11:01 +00:00
YK
e0b7a86313 perf(tree): worker pooling for account proofs (#18901)
Co-authored-by: Brian Picciano <me@mediocregopher.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-10-15 00:26:02 +00:00
Dan Cline
169a1fb97b fix(engine): flatten storage cache (#18880) 2025-10-14 19:12:55 +00:00
Arsenii Kulikov
c661cd2f75 refactor: unify Pipeline creation codepaths (#18955) 2025-10-14 16:20:21 +00:00
Forostovec
7aebea2f37 chore(evm): mark ExecuteOutput as unused and slated for removal (#18754) 2025-10-14 14:18:33 +00:00
Forostovec
0470ee8735 fix(stateless): enforce BLOCKHASH ancestor header limit (#18920) 2025-10-14 14:05:47 +00:00
drhgencer
cec30cd9f3 chore: remove unused imports in blockchain_provider (#18867)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-14 16:06:06 +02:00
Alvarez
9fa2779959 fix(network): prevent metric leak in outgoing message queue on session teardown (#18847) 2025-10-14 13:33:45 +00:00
Léa Narzis
5065890823 chore: bump otlp crates (#18984) 2025-10-14 09:34:20 +00:00
stevencartavia
2a441d6261 refactor: convert satisfy_base_fee_ids to use closure (#18979) 2025-10-14 09:29:01 +00:00
MozirDmitriy
221d585f08 chore(optimism): remove unnecessary Debug bounds from header generics (#18989) 2025-10-14 08:54:55 +00:00
stevencartavia
ab2b11f40e feat: allow otlp level to be configurable (#18981) 2025-10-14 00:41:22 +00:00
Léa Narzis
59ace58925 feat(cli): enable traces export via tracing-otlp cli arg (#18242)
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
2025-10-13 20:28:16 +00:00
Alvarez
9b0a2c37b4 perf(tests): remove redundant format! in ef-tests run_only (#18909) 2025-10-13 18:27:03 +00:00
Federico Gimenez
2041188744 chore(ci): update eest 7594 issue link in hive expected failures file (#18976) 2025-10-13 18:03:43 +00:00
Forostovec
55d294dc7f chore(rpc): Remove redundant U256::from in suggested_priority_fee (#18969) 2025-10-13 17:22:07 +00:00
Alexey Shekhirin
0f919a949e ci: remove reproducible build from release.yml (#18958) 2025-10-13 16:51:32 +00:00
maradini77
1dfd0ff772 fix: use max B256 for upper bound in empty-storage check (#18962) 2025-10-13 15:25:01 +00:00
sashaodessa
211e330eb9 fix: remove noisy stderr prints in ERA1 cleanup (EraClient::delete_outside_range) (#18895) 2025-10-13 14:00:40 +00:00
sashaodessa
ea65aca0d7 fix: misleading error message in db list: show actual table name (#18896) 2025-10-13 13:48:42 +00:00
Brian Picciano
edc8261913 fix(trie): Properly upsert into StoragesTrie in repair-trie (#18941) 2025-10-13 13:29:50 +00:00
YK
691b14bfca perf(tree): add elapsed time to parallel state root completion log (#18959) 2025-10-13 12:53:12 +00:00
maradini77
6c27b35e19 refactor(cli): use structured logging (tracing) in p2p command (#18957) 2025-10-13 12:24:58 +00:00
maradini77
4415bc5d7a refactor: replace println! with structured logging in test_vectors (#18956) 2025-10-13 11:51:19 +00:00
Emilia Hane
0f14980d88 chore(rpc): Moves SequencerMetrics into reth-optimism-rpc (#18921) 2025-10-13 10:24:55 +00:00
radik878
16ba9e8979 fix(payload): correct Debug label for PayloadTimestamp in PayloadServiceCommand (#18954) 2025-10-13 12:19:28 +02:00
Brian Picciano
16e79888ae fix(testsuite): Fix unused updates in e2e-test-utils (#18953) 2025-10-13 09:36:17 +00:00
Tilak Madichetti
99a5da2f91 fix(example): launch with debug capabilities (#18947) 2025-10-11 10:50:52 +00:00
Tilak Madichetti
b1d6c90fbb fix(examples): change method to launch with debug capabilities (#18946) 2025-10-11 10:50:31 +00:00
emmmm
5c18df9889 refactor: remove needless collect() calls in trie tests (#18937) 2025-10-10 16:23:10 +00:00
MIHAO PARK
aec3e3dcc5 chore(grafana): use precompile address as legend (#18913) 2025-10-10 12:26:47 +00:00
YK
397a30defb perf(tree): worker pooling for storage in multiproof generation (#18887)
Co-authored-by: Brian Picciano <me@mediocregopher.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-10-10 07:58:15 +00:00
Léa Narzis
d2070f4de3 feat: wait for new blocks when build is in progress (#18831)
Co-authored-by: Roman Hodulák <roman.hodulak@polyglot-software.com>
2025-10-09 16:42:59 +00:00
emmmm
6f96a32812 chore: align node_config threshold constant (#18914) 2025-10-09 11:18:49 +00:00
stevencartavia
df6afe9daa docs: duplicate comment in Eip4844PoolTransactionError (#18858) 2025-10-08 15:03:44 +00:00
Federico Gimenez
c78378a8ce ci: cache hive simulator images to reduce prepare-hive job time (#18899) 2025-10-08 14:53:25 +00:00
Merkel Tranjes
c0caaa17be refactor: replace collect().is_empty() with next().is_none() in tests (#18902)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-10-08 14:20:39 +00:00
Dan Cline
6770ba9eed feat(provider): add get_account_before_block to ChangesetReader (#18898) 2025-10-08 13:11:16 +00:00
William Nwoke
bed26238dc refactor(engine): separate concerns in on_forkchoice_updated for better maintainability (#18661)
Co-authored-by: Nathaniel Bajo <nathanielbajo@Nathaniels-MacBook-Pro.local>
Co-authored-by: YK <chiayongkang@hotmail.com>
Co-authored-by: Brian Picciano <me@mediocregopher.com>
2025-10-08 11:54:59 +00:00
radik878
1aa312c12b chore(node): simplify EngineApiExt bounds by removing redundant constraints (#18905) 2025-10-08 11:46:20 +00:00
Brian Picciano
273ee08443 fix(trie): Reveal extension child when extension is last remaining child of a branch (#18891) 2025-10-08 11:05:27 +00:00
Léa Narzis
b82ad07775 chore: make clippy happy (#18900) 2025-10-08 10:18:49 +00:00
Arsenii Kulikov
319a8dceb4 chore: relax ChainSpec impls (#18894) 2025-10-07 17:54:25 +00:00
Skylar Ray
029509cc42 refactor: eliminate redundant allocation in precompile cache example (#18886) 2025-10-07 11:34:35 +00:00
Forostovec
2f3e2c6c97 fix(era-utils): fix off-by-one for Excluded end bound in process_iter (#18731)
Co-authored-by: Roman Hodulák <roman.hodulak@polyglot-software.com>
2025-10-07 11:23:54 +00:00
josé v
83cec3793b docs: yellowpaper sections in consensus implementation (#18881) 2025-10-06 21:33:12 +00:00
Alexey Shekhirin
e9598ba5ac feat(storage): read headers and transactions only from static files (#18788) 2025-10-06 11:39:48 +00:00
sprites0
d77bfd89b4 feat: Use generic HeaderTy for reth db get static-file headers (#18870)
Co-authored-by: sprites0 <199826320+sprites0@users.noreply.github.com>
2025-10-06 10:15:52 +00:00
Skylar Ray
f54741c52b fix: streamline payload conversion in custom engine API (#18864) 2025-10-06 10:06:12 +00:00
Dan Cline
978b8a2cd3 docs(engine): fix outdated comment on TreeMetrics (#18855) 2025-10-06 09:10:51 +00:00
Federico Gimenez
b550387602 chore: update hive expected/ignored failures (#18863) 2025-10-05 11:55:28 +00:00
Arsenii Kulikov
78535b0747 feat: make ChainSpec generic over header (#18856) 2025-10-03 17:18:57 +00:00
Artyom Bakhtin
fe10c07852 feat: Add building and publishing of *.deb packages (#18615)
Signed-off-by: bakhtin <a@bakhtin.net>
2025-10-03 15:36:56 +00:00
Brian Picciano
d276ce5758 feat: OverlayStateProvider (#18822)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-03 14:21:27 +00:00
GarmashAlex
373576704b chore(cli): remove unnecessary ProviderFactory clone in db_ro_exec! (#18845) 2025-10-03 12:37:17 +00:00
MozirDmitriy
5c05beb404 chore: relax trait bounds for EmptyBodyStorage in storage API (#18842) 2025-10-03 12:36:44 +00:00
Arsenii Kulikov
871bc82eee chore: do not generate fuzz tests in Compact macro (#18849) 2025-10-03 11:58:44 +00:00
Hai | RISE
8effbf265b perf(multiproof): cache storage proof root of missed leaves (#18750) 2025-10-03 10:57:31 +00:00
Federico Gimenez
fafe44d386 feat(rpc): support custom transaction error types in EthApiError (#18844) 2025-10-02 18:55:21 +00:00
DaniPopes
73f50ee9a1 ci: cache zepter installation (#18843) 2025-10-02 18:46:27 +00:00
futreall
656c00e3d6 perf: optimize account cache updates to reduce duplicate lookups (#18825) 2025-10-02 12:34:44 +00:00
Matthias Seitz
1d1fea72b6 docs: apply spelling and grammar fixes (#18836)
Co-authored-by: Jennifer Paffrath <jenpaff0@gmail.com>
Co-authored-by: Max <max@digi.net>
2025-10-02 11:22:43 +00:00
Mablr
467420ec25 feat(evm): Make ConfigureEngineEvm methods faillible (#18827) 2025-10-02 11:16:40 +00:00
Matthias Seitz
3a6ff3ba93 perf: avoid hash copies (#18834) 2025-10-02 11:15:10 +00:00
MozirDmitriy
9b005f36ce feat(flashblocks): relax Sync bounds on Sink and connect futures (#18830) 2025-10-02 10:10:31 +00:00
Arsenii Kulikov
2029842f77 feat: integrate EvmEnv helpers (#18817) 2025-10-02 10:10:30 +00:00
sashass1315
661400e857 fix(ress): avoid panic on Missing trie updates in ress provider witness construction (#18796) 2025-10-02 09:45:36 +00:00
Galoretka
33bf2b2acc chore(node): remove no-op impl for LaunchContextWith WithComponents (#18821) 2025-10-01 20:16:42 +00:00
Matthias Seitz
4f56de535f fix: track inemorysize more accurately (#18820) 2025-10-01 16:29:27 +00:00
nethoxa
f98e706072 fix: Add eth69 status validation (#18819)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-01 16:06:01 +00:00
Matthias Seitz
80b4d26b9d chore: use exact size for account weight (#18816) 2025-10-01 14:46:03 +00:00
Matthias Seitz
a18f1a2e38 chore: use correct inflight metric (#18815) 2025-10-01 13:41:12 +00:00
MIHAO PARK
a2bde852bb feat(node): reduce the status logging (#18010) 2025-10-01 09:56:31 +00:00
YK
83de2137f2 refactor(engine): simplify validate_block_with_state (#18659)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-10-01 08:22:42 +00:00
Julio
5004c2e1a3 feat(op-reth): add optional state root calc for flashblocks (#18721)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-10-01 07:57:38 +00:00
Karl Yu
7af393bb35 chore: update ETHEREUM_BLOCK_GAS_LIMIT (#18779) 2025-10-01 07:14:39 +00:00
Skylar Ray
f813a52c80 feat: impl Debug for FnLauncher (#18807) 2025-10-01 07:09:06 +00:00
futreall
bafb482ca1 fix: poll the pinger timeout Sleep future (#18797) 2025-10-01 07:07:57 +00:00
Federico Gimenez
22f9708f6a fix(storage): clean up HeaderNumbers entries during block unwinds (#18790) 2025-09-30 16:41:36 +00:00
viktorking7
0694abcee8 fix: Prevent u64 timestamp wrap-around in LocalMiner (#18791) 2025-09-30 15:29:14 +00:00
Matthias Seitz
530e62d0e9 chore: bump alloy 1.0.37 (#18795) 2025-09-30 16:27:04 +02:00
Tim
05d17bfe04 chore: bump version to 1.8.2 (#18792)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-30 16:02:45 +02:00
Matthias Seitz
a53c6205cc fix: remove cancun check (#18787) 2025-09-30 12:07:07 +00:00
GarmashAlex
2d4635b53d fix: remove the leading hash comparison from RecoveredBlock<B>::PartialEq. (#18785) 2025-09-30 11:54:32 +00:00
Federico Gimenez
db524d158e fix(op-reth): forward pre-bedrock transaction RPC calls to historical endpoint (#18784) 2025-09-30 10:09:46 +00:00
Ignacio Hagopian
7e5e8b55b3 feat(stateless): enable test runs to return execution witness (#18740)
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Co-authored-by: Kevaundray Wedderburn <kevtheappdev@gmail.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-30 08:44:02 +00:00
Yichi Zhang
0da9fabf87 fix(transaction-pool): Fix wrong assertion (#18778) 2025-09-30 08:14:43 +00:00
Andrea Simeoni
2cbdb42c2c feat(op-reth): custom FlashBlock decoder from bytes (#18770) 2025-09-30 08:13:01 +00:00
dustinjake
b3cbfa4ced feat(flashblocks): additional pending flashblock data (#18776) 2025-09-30 08:08:39 +00:00
YK
b8c16e392a refactor(engine): small nits - remove shallow abstraction for decoded_storage_proof (#18780) 2025-09-30 07:57:11 +00:00
futreall
2cf9fc8f54 refactor: remove redundant Setup::new() method (#18781) 2025-09-30 07:56:19 +00:00
Ragnar
121c0dedf8 feat(eth-wire): implement disconnect on Status message after handshake (#18773) 2025-09-29 19:35:49 +00:00
Alexey Shekhirin
058ffdc21e feat(storage): write headers and transactions only to static files (#18681) 2025-09-29 17:04:59 +00:00
James Niken
564e3a67fc fix: correct TxTypeCustom extended identifier decoding (#18769) 2025-09-29 14:38:37 +00:00
Merkel Tranjes
b940d0a9fb fix: prevent integer underflow in pipeline unwind target calculation (#18743) 2025-09-29 11:54:28 +00:00
sashass1315
18775914a4 fix(primitives-traits): use size_of::<H>() for ommers capacity in BlockBody (#18764) 2025-09-29 09:49:30 +00:00
YK
ec4e6aafde perf: optimize Optimism deposit transaction prewarming (#18327) 2025-09-29 09:06:17 +00:00
radik878
48b725aec2 chore(engine): remove unnecessary ChainSpecProvider bound from invalid block witness hook (#18760) 2025-09-28 16:30:28 +00:00
github-actions[bot]
c98833ba14 chore(deps): weekly cargo update (#18757)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-28 13:33:13 +02:00
Matthias Seitz
850083dbde chore: remove doc_auto_cfg feature (#18758) 2025-09-28 12:53:45 +02:00
nethoxa
abae566f13 fix(rpc): fix eth_config impl (#18744)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-28 12:42:08 +02:00
Marcin Papież
efbff54ea6 docs: add note to launch on --dev mode (#18745) 2025-09-28 12:41:45 +02:00
Adrian
be326fe047 test: add missing Drop trait tests for CancelOnDrop (#18749) 2025-09-28 12:38:37 +02:00
MIHAO PARK
722507ed41 docs(ethereum): extend run with debug.rpc-consensus-url (#18747) 2025-09-27 07:14:59 +00:00
stevencartavia
1addf61a23 feat: keep track of most recently emitted range update and change int… (#18722) 2025-09-26 16:02:38 +00:00
VolodymyrBg
95e8a65d33 chore(trie): demote verbose proof debug logs to TRACE (#18738) 2025-09-26 15:08:31 +00:00
Galoretka
c9fea939a1 fix(chain-state): remove redundant transaction clone in test utils (#18710)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-26 14:20:34 +00:00
MIHAO PARK
74c4cdbf09 fix(rpc-engine): don't fetch the pruned block (#18589) 2025-09-26 14:17:10 +00:00
MIHAO PARK
0299160e93 feat(rpc): merge the header not found and resource not found (#18657) 2025-09-26 14:09:23 +00:00
Mablr
6a50aa3ea5 feat: make more EVM and RPC conversions fallible (#18685) 2025-09-26 13:56:46 +00:00
Alexey Shekhirin
057c71281f feat(cli): configure multiproof chunking via arguments (#18736) 2025-09-26 13:00:16 +00:00
Waiting
5dc2857713 feat(downloaders): add file-client feature gate (#18707) 2025-09-26 12:59:08 +00:00
Delweng
597fa73023 fix(rpc/engine): check osaka in getBlobsV1 (#18669)
Signed-off-by: Delweng <delweng@gmail.com>
2025-09-26 15:14:09 +02:00
anim001k
8852269a7d fix: Apply WS CORS regardless of HTTP being enabled (#18729) 2025-09-26 11:53:52 +00:00
Brian Picciano
ff4cc6e3ba chore: Accept range in HashedPostState::from_reverts (#18728) 2025-09-26 11:29:37 +00:00
Hai | RISE
8d44bebf8d perf(multiproof): do not chunk more tasks when task queue is full (#18727) 2025-09-26 11:15:02 +00:00
Matthias Seitz
4b134c3a46 fix: increase backoff timeout (#18733) 2025-09-26 11:04:40 +00:00
viktorking7
aa192c255b fix: Bearer token parsing vulnerability (#18712) 2025-09-25 20:08:00 +00:00
Matthias Seitz
b6cf855738 docs: add note about v5 (#18701) 2025-09-25 19:03:53 +00:00
drhgencer
284d1b377f perf: avoid redundant bytecode hash calculation in RPC provider (#18711) 2025-09-25 18:24:45 +00:00
0xKitsune
0e4e32fb16 chore: update spawn maintenance tasks vis (#18709) 2025-09-25 16:55:08 +00:00
YK
8f881789b7 perf(engine): reduce cloning on terminate caching (#18693) 2025-09-25 14:58:25 +00:00
YK
a047a055ab chore: bump rust to edition 2024 (#18692) 2025-09-25 12:18:51 +00:00
Galoretka
9a26947db6 fix(primitives-traits): delegate is_create for Extended::Other to fix create-detection (#18699) 2025-09-25 10:34:32 +00:00
dustinjake
0f46e38a7d chore(flashblocks): flasblock service metrics (#18697) 2025-09-25 10:13:20 +00:00
sw4sy
a31dce9b3c feat(net): added check for non zero latest_hash in BlockRangeUpdate (#18695)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-25 08:11:43 +00:00
josé v
6cdfc48bc8 feat(flashblocks): prefill cache on canonical tip updates (#18691) 2025-09-25 07:55:01 +00:00
Forostovec
4d609847bf fix(engine): remove redundant parent_to_child removal during eviction (#18648) 2025-09-25 06:53:49 +00:00
Haotian
8f804d385d feat: node import support importing gzip compressed file (#17877)
Signed-off-by: tmelhao <tmel0103@gmail.com>
Co-authored-by: tmelhao <tmel0103@gmail.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-24 21:08:54 +00:00
MIHAO PARK
bdc59799d0 fix(cli): replace unwrap with error propagation in merkle stage (#18656) 2025-09-24 20:35:10 +00:00
GarmashAlex
3b0d98f346 fix(engine): align sparse trie removal log target with engine::root::sparse (#18686) 2025-09-24 17:48:54 +00:00
radik878
8e488a730a chore(engine): remove unused EngineServiceError from engine service (#18666)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-24 14:35:27 +00:00
Arsenii Kulikov
468925fcaa feat: support empty SUPPORTED_CHAINS for ChainSpecParser (#18679) 2025-09-24 14:27:07 +00:00
Matthias Seitz
00e51575eb test: add unwind parse test (#18677) 2025-09-24 15:51:50 +02:00
morito
e6050e0332 docs: some fixes on discv4 docs (#18601)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-24 12:41:34 +00:00
phrwlk
6631fc4e82 feat(net): correct per-response size metric to avoid capacity/empty-block inflation (#18553) 2025-09-24 12:21:51 +00:00
Joly
27c0b7b8a0 chore: enable all Ethereum protocol versions instead of hardcoding (#18065)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-24 12:19:54 +00:00
Andrea Simeoni
324cce3461 feat(bootnode): Persists the discovery secret key (#18643) 2025-09-24 12:14:59 +00:00
Arsenii Kulikov
7fb24e57a8 refactor: simplify EthApiSpeс trait (#18674) 2025-09-24 14:13:49 +02:00
Hai | RISE
1a68d8e968 feat(db): add MDBX put-append for fast ordered puts (#18603) 2025-09-24 11:48:50 +00:00
Starkey
f07d9248b9 fix: avoid panic in new-payload-fcu benchmark (#18602) 2025-09-24 11:37:14 +00:00
YK
a80f0c83a3 chore(revert): add tokio runtime with custom thread naming (#18670) 2025-09-24 10:52:51 +00:00
Tim
70fdd2248e chore: update voc.config.to text to v1.8.1 (#18667) 2025-09-24 09:51:09 +00:00
crazykissshout
aeaa8ec5a2 docs(db): correct misleading test comments in post_state.rs (#18664)
Co-authored-by: YK <chiayongkang@hotmail.com>
2025-09-24 09:03:07 +00:00
Gengar
f364f7a813 docs: add comprehensive e2e test actions reference documentation (#18621)
Co-authored-by: YK <chiayongkang@hotmail.com>
2025-09-24 03:27:02 +00:00
Arsenii Kulikov
96c1566d9b chore: support custom transaction types in EthTransactionValidator (#18655) 2025-09-23 22:23:09 +00:00
Alvarez
4779fea9d4 docs: rm 8MB stack size comment in BlockingTaskPool (#18616) 2025-09-23 21:43:07 +00:00
GarmashAlex
5856c2e9f0 fix(payload): reversed-order test for validate_execution_requests (#18593)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-23 20:34:18 +00:00
Alvarez
8eaadf52d8 fix: Prevent potential underflow in static file header healing (#18628) 2025-09-23 20:32:46 +00:00
crazykissshout
a7632c7431 fix(engine): correct misleading test comments in cached_state.rs (#18652) 2025-09-23 20:12:26 +00:00
Federico Gimenez
4cc50f9799 feat(e2e): add beacon consensus handle to NodeClient (#18632) 2025-09-23 17:45:09 +00:00
YK
064694b2df refactor(engine): simplify on_new_payload (#18613) 2025-09-23 16:04:19 +00:00
Matthias Seitz
e6608be51e chore: release 1.8.1 (#18646) 2025-09-23 17:41:38 +02:00
Matthias Seitz
44aa0fbb0e fix: Revert "chore: disable fee charge in env" (#18645) 2025-09-23 17:40:40 +02:00
dependabot[bot]
6fdf6c4492 chore(deps): bump CodSpeedHQ/action from 3 to 4 (#18333)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-23 14:07:44 +00:00
Tim
e613ee9e85 chore: update voc.config.to text to v1.8.0 (#18644) 2025-09-23 14:07:29 +00:00
Tim
132f5b5204 chore: update version to 1.8.0 in Cargo.toml (#18638)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-23 13:48:38 +00:00
Matthias Seitz
faaebe7f6d fix: check request gas limit before (#18639) 2025-09-23 12:21:59 +00:00
YK
088a0d44c2 chore(observability): add tokio runtime with custom thread naming (#18635)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-09-23 12:05:35 +00:00
Tim
4c9942b920 docs: update dashboard table and rpc urls (#18637) 2025-09-23 12:01:19 +00:00
Matthias Seitz
70a8c06773 feat: add osaka+bpo timestamps (#18627)
Co-authored-by: Brian Picciano <me@mediocregopher.com>
2025-09-23 11:06:52 +00:00
Matthias Seitz
ee834fb892 chore: disable fee charge in env (#18634) 2025-09-23 13:01:20 +02:00
Matthias Seitz
f225751c12 chore: bump inspectors 0.30 (#18633) 2025-09-23 13:01:08 +02:00
YK
7dc3aea930 chore(revert): revert tokio runtime with custom thread naming (#18631) 2025-09-23 10:20:44 +00:00
Matthias Seitz
87c75b9836 chore: bump deps (#18630) 2025-09-23 10:03:07 +00:00
YK
2ec3671633 chore(observability): add tokio runtime with custom thread naming (#18623) 2025-09-23 09:04:54 +00:00
MozirDmitriy
b27a927413 chore(primitive-traits): remove redundant auto-trait bounds from FullNodePrimitives (#18626)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-23 09:03:12 +00:00
Dmitry
e3cc6e2ea5 docs: fix incorrect RPC method names in trace calls (#18619) 2025-09-23 07:56:11 +00:00
Andrea Simeoni
dfab5f9646 fix(cli): bootnode default address (#18617) 2025-09-22 20:19:40 +00:00
Galoretka
87078e9205 fix(primitives-traits): simplify Rayon bounds and fix docs (#18620) 2025-09-22 19:48:10 +00:00
josé v
9e3246e695 chore: specialize send_raw_transaction_sync for op-reth with flashblocks support (#18586)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-22 13:43:57 +00:00
VolodymyrBg
60658be734 fix(handshake): validate peer TD from their_status_message during eth handshake (#18611) 2025-09-22 13:33:55 +02:00
Matthias Seitz
0bd2097995 chore: enforce max tx gas limit on estimate and accesslit (#18612) 2025-09-22 13:04:26 +02:00
Matthias Seitz
39d5563ce8 fix: disable block gas limit (#18583) 2025-09-22 10:07:03 +00:00
Brian Picciano
79c71b8692 chore: Remove reth recover storage-tries sub-command (#18580) 2025-09-22 10:04:40 +00:00
emmmm
9806e07cf8 fix: replace tx_hash method with TxHashRef trait bound (#18357) (#18362)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-22 09:31:17 +00:00
Matthias Seitz
3ebfd7a25e test: add test case for op tx env conversion (#18581) 2025-09-22 11:39:28 +02:00
YK
36107c60ab fix(cache): Ensure execution cache remains locked until updated (#18564)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-09-22 11:38:02 +02:00
YK
95f1931c59 test(engine): add new payload handling tests for canonical insertion and invalid ancestors (#18608) 2025-09-22 08:38:53 +00:00
crStiv
4ddf3ddb45 docs: multiple small textual defects (#18598) 2025-09-21 08:36:24 +00:00
github-actions[bot]
aeb6eddba0 chore(deps): weekly cargo update (#18600)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-21 07:37:18 +00:00
Federico Gimenez
ff59089094 chore(ci): unpin teku image for kurtosis-op (#18595) 2025-09-20 13:59:01 +00:00
Federico Gimenez
aead6c17c5 chore(ci): update expected and ignored hive tests (#18594) 2025-09-20 13:58:41 +00:00
Hai | RISE
55cbefe836 perf(persistence): lookup segment operation once (#18588) 2025-09-20 07:22:31 +00:00
William Nwoke
3655dc7f09 feat(rpc): make send_raw_transaction_sync timeout configurable (#18558)
Co-authored-by: Nathaniel Bajo <nathanielbajo@Nathaniels-MacBook-Pro.local>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-20 05:50:56 +00:00
Matthias Seitz
379db45b40 fix: use timestamp derived max blob count on launch (#18590) 2025-09-20 05:41:56 +00:00
VolodymyrBg
fa531761c4 chore(payload-builder): relax Sync bounds on resolve futures (#18585) 2025-09-19 20:22:53 +00:00
Brian Picciano
ff51faaeac chore(db): Simplifications to trie-related storage-api methods (#18579) 2025-09-19 15:41:32 +00:00
YK
8f4cc90ef9 chore: clippy manual_string_new warning in version.rs (#18576) 2025-09-19 15:03:20 +00:00
Brian Picciano
ebe1a8b014 chore(trie): Use Vec<Option<...>> in InMemoryTrieCursor (#18479) 2025-09-19 13:24:46 +00:00
Dharm Singh
d6160de610 fix(rpc): return empty log set for invalid filter block ranges (#18112)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-19 11:47:38 +00:00
YK
8aeebe10ff fix(txpool): prevent double-processing of tx pool tier (#18446)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-19 11:10:01 +00:00
Alexey Shekhirin
5bc507bfaf fix(reth-bench): do not panic on empty results (#18570) 2025-09-19 10:37:50 +00:00
かとり
4e1c552d3a fix(optimism): always enable interop maintenance task if activated (#18563) 2025-09-19 09:57:49 +00:00
0xOsiris
4fcc4457c1 chore(evm): add public constructor to BlockAssemblerInput (#18559) 2025-09-19 11:59:24 +02:00
Arsenii Kulikov
c9a95d085d feat: add Future AT to LaunchNode and allow customizing local attributes builder (#18556) 2025-09-19 09:34:49 +00:00
Matthias Seitz
4e78f956fd chore: map NaN to 0.0 (#18560) 2025-09-18 22:35:48 +00:00
MozirDmitriy
df9b7a079b chore(chainspec): reuse local hardforks in DEV instead of cloning again (#18557) 2025-09-18 17:38:38 +00:00
MIHAO PARK
e2aa41733c chore(docker): add FEATURES for op dockerfile (#18489) 2025-09-18 13:15:33 +00:00
Federico Gimenez
6f385d0a01 chore(consensus): update EIP-7825 error message format (#18549) 2025-09-18 13:10:21 +00:00
MIHAO PARK
f9e5030386 docs(op): decompress the state file before init-state (#18416) 2025-09-18 15:18:21 +02:00
stevencartavia
ce6199abf6 feat: tree config setting for unwinding canonical header (#18420) 2025-09-18 12:59:08 +00:00
MIHAO PARK
70d634a3f8 feat(rpc): add admin_clearTxpool api (#18539) 2025-09-18 12:58:20 +00:00
Federico Gimenez
ea500f6af9 chore(ci): bump hive timeout (#18544) 2025-09-18 12:52:18 +00:00
Roman Hodulák
e8d32a5491 feat(rpc): Add convert_receipt_with_block method to ReceiptConverter (#18542)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-18 11:06:52 +00:00
Matthias Seitz
ece847287a chore: add cache traces (#18538) 2025-09-18 09:21:44 +00:00
Copilot
64b4ae60f5 docs: document critical cache safety assumptions in ExecutionCache (#18536)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: yongkangc <46377366+yongkangc@users.noreply.github.com>
Co-authored-by: YK <chiayongkang@hotmail.com>
2025-09-18 08:52:13 +00:00
Julio
59cff107bc feat(op-reth): initial setup FlashBlockConsensusClient engine sidecar (#18443) 2025-09-18 08:13:22 +00:00
Dharm Singh
870389c5d6 refactor: EmptyBodyStorage block reader logic (#18508)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-17 23:51:27 +00:00
MIHAO PARK
d357d2acb3 feat(node): rename debug.rpc-consensus-ws to debug-rpc-consensus-url to suport HTTP (#18027)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-17 23:10:55 +00:00
Mablr
2b68d3a424 fix(rpc): use flashblock when preparing tx response on gettxreceipt (#18530) 2025-09-17 20:39:37 +00:00
Federico Gimenez
6bf405a143 chore(ci): bump hive eest tests to v5.1.0 (#18528) 2025-09-17 19:15:17 +00:00
leniram159
4a958f41b8 fix: use noopprovider for pending block state root (#18523) 2025-09-17 17:24:38 +00:00
Roman Hodulák
6e6a497ef2 refactor(evm): Replace revm_spec* functions with alloy-evm and alloy-op-evm versions (#18526) 2025-09-17 16:46:21 +00:00
Roman Hodulák
5a39e57e47 deps: Upgrade alloy and alloy-evm versions 1.0.30 => 1.0.32 and 0.21.0 => 0.21.1 respectively (#18525) 2025-09-17 15:53:24 +00:00
crazykissshout
4b4b122e75 docs(engine): improve cache naming and documentation (#18457)
Co-authored-by: YK <chiayongkang@hotmail.com>
2025-09-17 14:45:26 +00:00
Hai | RISE
98ce04d5e0 feat: relax OpEngineValidatorBuilder for more custom payload types (#18520) 2025-09-17 14:37:07 +00:00
Brian Picciano
d9c9810266 fix(trie): Don't run repair-trie if MerkleExecute is incomplete (#18497) 2025-09-17 14:32:02 +00:00
spencer
193f699057 chore(engine): remove calldata exception workaround (#18521) 2025-09-17 14:25:42 +00:00
Alexey Shekhirin
584d7164fd feat(engine): fallback for when both state root task and parallel state root failed (#18519) 2025-09-17 13:31:26 +00:00
Roman Hodulák
5c5b21e489 feat(optimism): Implement local_pending_state for RPC that uses pending_flashblock (#18518) 2025-09-17 12:59:27 +00:00
theo
fabf3e84d4 feat(op/jovian): implement min base fee in op-reth. bump alloy, alloy-evm deps. (#18407)
Co-authored-by: Emilia Hane <emiliaha95@gmail.com>
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-09-17 12:47:01 +00:00
VolodymyrBg
8a3d984c11 fix(docs): correct BlockBody root docs and RecoveredBlock “safer variant” references (#18510) 2025-09-17 12:25:27 +00:00
Federico Gimenez
f113a97a78 chore(ci): run eest osaka tests on hive workflow (#18516) 2025-09-17 12:11:25 +00:00
YK
31ce037a25 chore: add myself to CODEOWNERS (#18514) 2025-09-17 10:42:32 +00:00
YK
088eb6c463 feat(metrics): add transaction error counter for prewarming (#18509) 2025-09-17 09:57:30 +00:00
Arsenii Kulikov
04c5820689 fix: don't override existing tables in create_tables_for (#18511)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-17 09:56:35 +00:00
Brian Picciano
9c892b0233 chore: add myself to CODEOWNERS for engine and stages (#18512) 2025-09-17 09:55:11 +00:00
Arsenii Kulikov
9fc89495d0 fix: don't require closure to be Debug (#18507) 2025-09-16 22:51:29 +00:00
sashass1315
c45817c1f2 chore(engine): avoid panic on mpsc send in sparse trie worker (#18502) 2025-09-16 20:39:33 +00:00
Arsenii Kulikov
7296fc68b6 feat: relax EthBlockAssembler (#18505) 2025-09-16 19:38:35 +00:00
Arsenii Kulikov
7af829ed37 feat: make EthBuiltPayload generic over NodePrimitives (#18504) 2025-09-16 19:12:11 +00:00
Arsenii Kulikov
bf58089286 feat: more flexible rpc receipts (#18501) 2025-09-16 20:49:41 +02:00
Matthias Seitz
5274f095fe chore: skip prewarm transact errors (#18498) 2025-09-16 15:13:20 +00:00
MozirDmitriy
1185514c1e fix(engine): exit MultiProofTask loop on closed internal channel (#18490) 2025-09-16 14:22:45 +00:00
Richard Janis Goldschmidt
847330cdfc fix(cli): disallow --instance 0 (#18496) 2025-09-16 14:16:39 +00:00
wizard
d1c966020b docs: fix incorrect transaction type count (#18437) 2025-09-16 12:20:03 +00:00
VolodymyrBg
18052836fe docs(engine): fix LiveSync target doc and clarify disable-parallel-sparse-trie semantics (#18478) 2025-09-16 12:05:39 +00:00
Matthias Seitz
1697826fdb chore: deconstruct non-exhaustive (#18492) 2025-09-16 12:00:37 +00:00
Matthias Seitz
976939ab6b chore: update superchain commit (#18481)
Co-authored-by: Federico Gimenez <federico.gimenez@gmail.com>
2025-09-16 10:55:57 +00:00
Nathaniel Bajo
05008e2841 feat(op-reth): specialize get_transaction_receipt to check pending flashblocks (#18374)
Co-authored-by: Nathaniel Bajo <nathanielbajo@Nathaniels-MacBook-Pro.local>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2025-09-16 02:07:01 +00:00
Matthias Seitz
8e65a1d1a2 fix: missing generic type hint for cursor (#18483) 2025-09-15 23:32:19 +00:00
CPerezz
b7e9f7608e feat(network): add shadowfork block hash filtering for peers (#18361)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-15 22:14:04 +00:00
Arsenii Kulikov
5f38ff7981 feat: Block::iter_recovered (#18476) 2025-09-15 20:26:19 +00:00
Alexey Shekhirin
5844ff7b17 feat(storage): bump MDBX map size to 8TB (#18360) 2025-09-15 16:42:37 +00:00
Matthias Seitz
2dabb23331 fix(rpc): disable tx gas limit in calls (#18473) 2025-09-15 15:53:23 +00:00
Federico Gimenez
7cf239ab59 feat: add CliApp wrapper for ethereum CLI configuration and execution (#18458) 2025-09-15 15:31:37 +00:00
Federico Gimenez
e578b1b933 chore(ci): update ignored hive tests (#18412) 2025-09-15 14:18:00 +00:00
MIHAO PARK
ec2a898ac6 fix(rpc): add validation for missing headers in debug execution witness (#18444) 2025-09-15 14:15:35 +00:00
Hai | RISE
ef85d93cd7 perf(db): open MDBX DBIs only once at startup (#18424) 2025-09-15 11:57:01 +00:00
MozirDmitriy
d2b9c571a2 fix(engine): remove redundant method-level where bound in InvalidBlockWitnessHook (#18459) 2025-09-15 10:39:20 +00:00
YK
d61349beb2 fix(engine): perform cache updates with guard (#18435) 2025-09-15 10:27:58 +00:00
Fredrik
7d5415a608 perf: Enforce EIP-7825 transaction gas limit for Osaka (#18439)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-15 10:25:16 +00:00
Federico Gimenez
5516ad2d4f chore(ci): unpin kurtosis op package (#18456) 2025-09-15 09:23:02 +00:00
crStiv
1b08843bc5 docs: multiple small textual defects (#18434)
Co-authored-by: YK <chiayongkang@hotmail.com>
2025-09-14 12:08:43 +00:00
Matthias Seitz
96f8454d42 chore: remove type aliases (#18433) 2025-09-14 12:23:46 +02:00
github-actions[bot]
2408586a51 chore(deps): weekly cargo update (#18431)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
2025-09-14 11:27:26 +02:00
stevencartavia
27e4a05cf0 chore: move and rename PendingBlockAndReceipts to BlockAndReceipts (#18430) 2025-09-14 07:41:43 +00:00
lipperhey
1bd6cc21c2 chore: clean up TS warnings in search index & file finder (#18426) 2025-09-13 11:54:02 +00:00
Matthias Seitz
33c75e8e52 chore: add state and response to miner error (#18422) 2025-09-13 13:32:24 +02:00
stevencartavia
99b6dc7986 feat: add helper to PendingBlockAndReceipts (#18423) 2025-09-13 09:51:17 +00:00
stevencartavia
7694b9dee3 feat: fn recovered_tx to indexedTx (#18421) 2025-09-13 10:55:19 +02:00
Hai | RISE
f66e197171 chore(storage): remove unused primed_dbis (#18415) 2025-09-13 07:32:22 +00:00
Hai | RISE
bac0e1f83f perf: downsize mempool tx priority from U256 to u128 (#18413) 2025-09-13 07:30:46 +00:00
TMOT
e276480728 feat(observability): add phase-level observablity to newPayload processing (#18308)
Co-authored-by: YK <chiayongkang@hotmail.com>
Co-authored-by: Dan Cline <6798349+Rjected@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-13 02:01:48 +00:00
Matthias Seitz
44a48ab9fd fix: dont update canon chain to ancestor for opstack (#18410) 2025-09-12 19:36:05 +02:00
かとり
72c2d1b6a0 feat(txpool): break down queued transaction states into specific reasons (#18106)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-12 13:03:41 +00:00
Hai | RISE
51bf7e37e2 perf(db): reuse MDBX DBIs for the same tx (#18292) 2025-09-12 12:34:52 +00:00
Federico Gimenez
bd387cd495 chore: update e2e-test-utils code owners (#18397) 2025-09-12 10:41:12 +00:00
Snezhkko
82fb54763c fix(e2e): persist accepted header in CheckPayloadAccepted and align timestamp (#18275)
Co-authored-by: Federico Gimenez <federico.gimenez@gmail.com>
Co-authored-by: Federico Gimenez <fgimenez@users.noreply.github.com>
2025-09-12 10:41:04 +00:00
Cypher Pepe
87444ef8d0 chore: fixed broken link in history-expiry.mdx (#18400) 2025-09-12 10:38:39 +00:00
leniram159
6d4a1a3ccf chore: use decode_2718_exact for recover raw txs (#18381) 2025-09-12 08:40:17 +00:00
Hai | RISE
40a9954a8e fix: still use real chain id for no-op network (#18382) 2025-09-12 08:30:37 +00:00
stevencartavia
3e4c0cc402 feat: replace PendingBlockAndReceipts tuple with dedicated struct (#18395) 2025-09-11 22:32:09 +00:00
Federico Gimenez
f3aa57a10e fix: map EIP-7623 gas floor errors to expected exception type for test compatibility (#18389) 2025-09-11 20:15:53 +00:00
Yash Atreya
edc1ae8f4d fix(docs): mv search-index to dist from .vocs (#18390) 2025-09-11 14:49:19 +00:00
Yash Atreya
8c2d5cc484 fix(docs): disable jekyll which removes the search-index (#18388) 2025-09-11 12:37:07 +00:00
Matthias Seitz
9d3564ecba fix: relax nonce gap rule if configured (#18385) 2025-09-11 11:39:50 +00:00
Arsenii Kulikov
60568cca8f feat: add helper aliases for node adapters (#18366) 2025-09-11 07:55:13 +00:00
Arsenii Kulikov
a80ed916b1 refactor!: more type-safety in cli (#18375) 2025-09-11 07:54:34 +00:00
Brian Picciano
967a6fb1d5 perf(trie): Use ParallelSparseTrie (if enabled) for storage tries (#17959) 2025-09-10 22:51:52 +00:00
Matthias Seitz
f2350e509e fix: check payload id (#18370) 2025-09-10 18:46:48 +00:00
Federico Gimenez
17a41a2463 feat: bump hive eest tests (#18013) 2025-09-10 18:30:39 +00:00
Federico Gimenez
424974ca37 fix(engine): avoid block fetching inconsistencies for checks during reorgs (#18368) 2025-09-10 17:44:38 +00:00
Federico Gimenez
d6a92287ed feat(engine): check header validity after invalid transaction (#18356) 2025-09-10 12:00:28 +00:00
Yash Atreya
e94658f792 fix(docs): include .vocs to retain search-index (#18363) 2025-09-10 10:22:03 +00:00
Matthias Seitz
700f2e101a feat: add some ethapi builder fns (#18358) 2025-09-10 09:12:02 +00:00
malik
a3aaccd34a perf: optimize canonical_hashes_range with Vec::with_capacity pre-allocation + benchmark (#18072)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-10 07:36:51 +00:00
Rez
fe236cd571 fix: add is_osaka check before erroring in default_ethereum_payload (#18355) 2025-09-10 07:06:12 +00:00
Léa Narzis
3ce0a38108 fix: fix search in vocs doc (#18354) 2025-09-09 20:42:57 +00:00
Emilia Hane
4c363fe1aa feat(op-sdk): custom precompiles (#18350) 2025-09-09 20:04:41 +00:00
Brian Picciano
2fa52f32f4 fix(prune): TransactionLookup pruning issues with pre-merge expiry (#18348) 2025-09-09 16:55:17 +00:00
Arsenii Kulikov
90aa99cb3c feat: support customizable RPC namespace parsers (#18160)
Co-authored-by: Federico Gimenez <federico.gimenez@gmail.com>
2025-09-09 14:17:43 +00:00
Ignacio Hagopian
394a53d7b0 feat(stateless): Run EEST tests in stateless block validator & bug fixes (#18140)
Signed-off-by: Ignacio Hagopian <jsign.uy@gmail.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-09 12:48:14 +00:00
Matthias Seitz
4fdc1ceb0c refactor(revm): (#18150) use hardfork activation helpers (#18349)
Co-authored-by: Waiting-Chai <1753609696@qq.com>
2025-09-09 12:47:17 +00:00
malik
1423a30e15 perf: use debug_assert for parked pool lookup (#17712)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-09 12:45:11 +00:00
かとり
b7c2b562e1 fix(stages): implement entities checkpoint update in merkle stage unwind (#18131) 2025-09-09 12:02:52 +00:00
Federico Gimenez
6c9c96c132 fix(ci): pin teku image to fix kurtosis-op build (#18345) 2025-09-09 11:32:13 +00:00
nk_ysg
bfb37da2a9 perf(reth-engine-local): use VecDeque reduce removal operations (#18198) 2025-09-09 11:16:56 +00:00
theo
86eaa6f285 feat(op-reth/flashblocks): subscribe to the flashblock sequences produced (#18276)
Co-authored-by: julio4 <30329843+julio4@users.noreply.github.com>
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-09-09 11:10:30 +00:00
Matthias Seitz
aa5e6ad417 fix: properly compute genesis hash (#18300) 2025-09-09 12:15:57 +02:00
Hai | RISE
64afc1e549 perf(merkle-stage): only fetch checkpoint in the branch that needs it (#18339) 2025-09-09 09:04:44 +00:00
Arsenii Kulikov
1e491bc85e feat: cache latest built payload (#18324) 2025-09-09 07:55:34 +00:00
dependabot[bot]
0d13d7f4ff chore(deps): bump actions/stale from 9 to 10 (#18335)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 07:50:59 +00:00
dependabot[bot]
e079ddc7a5 chore(deps): bump actions/github-script from 7 to 8 (#18334)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 07:50:45 +00:00
dependabot[bot]
4b29f5fafe chore(deps): bump actions/setup-go from 5 to 6 (#18332)
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-09 07:50:16 +00:00
Brian Picciano
b4beab1a83 chore(trie): use read-only db handle during repair-trie dry-runs (#18328) 2025-09-09 06:56:26 +00:00
Matthias Seitz
a35b299ae5 docs: update public dashboards (#18331) 2025-09-09 00:34:29 +02:00
Matthias Seitz
23c2dcac9a chore: bump docs version 1.7.0 (#18326) 2025-09-08 23:27:08 +02:00
1191 changed files with 81915 additions and 23570 deletions

View File

@@ -6,8 +6,21 @@ slow-timeout = { period = "30s", terminate-after = 4 }
filter = "test(general_state_tests)"
slow-timeout = { period = "1m", terminate-after = 10 }
[[profile.default.overrides]]
filter = "test(eest_fixtures)"
slow-timeout = { period = "2m", terminate-after = 10 }
# E2E tests using the testsuite framework from crates/e2e-test-utils
# These tests are located in tests/e2e-testsuite/ directories across various crates
[[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",
"--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

26
.github/CODEOWNERS vendored
View File

@@ -1,13 +1,12 @@
* @gakonst
crates/blockchain-tree-api/ @rakita @rkrasiuk @mattsse @Rjected
crates/blockchain-tree/ @rakita @rkrasiuk @mattsse @Rjected
crates/chain-state/ @fgimenez @mattsse @rkrasiuk
crates/blockchain-tree-api/ @rakita @mattsse @Rjected
crates/blockchain-tree/ @rakita @mattsse @Rjected
crates/chain-state/ @fgimenez @mattsse
crates/chainspec/ @Rjected @joshieDo @mattsse
crates/cli/ @mattsse
crates/consensus/ @rkrasiuk @mattsse @Rjected
crates/e2e-test-utils/ @mattsse @Rjected
crates/engine @rkrasiuk @mattsse @Rjected
crates/engine/ @rkrasiuk @mattsse @Rjected @fgimenez
crates/consensus/ @mattsse @Rjected
crates/e2e-test-utils/ @mattsse @Rjected @klkvr @fgimenez
crates/engine/ @mattsse @Rjected @fgimenez @mediocregopher @yongkangc
crates/era/ @mattsse @RomanHodulak
crates/errors/ @mattsse
crates/ethereum-forks/ @mattsse @Rjected
@@ -16,17 +15,17 @@ crates/etl/ @joshieDo @shekhirin
crates/evm/ @rakita @mattsse @Rjected
crates/exex/ @shekhirin
crates/net/ @mattsse @Rjected
crates/net/downloaders/ @rkrasiuk
crates/net/downloaders/ @Rjected
crates/node/ @mattsse @Rjected @klkvr
crates/optimism/ @mattsse @Rjected @fgimenez
crates/payload/ @mattsse @Rjected
crates/primitives-traits/ @Rjected @RomanHodulak @mattsse @klkvr
crates/primitives/ @Rjected @mattsse @klkvr
crates/prune/ @shekhirin @joshieDo
crates/ress @rkrasiuk
crates/ress @shekhirin @Rjected
crates/revm/ @mattsse @rakita
crates/rpc/ @mattsse @Rjected @RomanHodulak
crates/stages/ @rkrasiuk @shekhirin
crates/stages/ @shekhirin @mediocregopher
crates/static-file/ @joshieDo @shekhirin
crates/storage/codecs/ @joshieDo
crates/storage/db-api/ @joshieDo @rakita
@@ -36,10 +35,11 @@ crates/storage/errors/ @rakita
crates/storage/libmdbx-rs/ @rakita @shekhirin
crates/storage/nippy-jar/ @joshieDo @shekhirin
crates/storage/provider/ @rakita @joshieDo @shekhirin
crates/storage/storage-api/ @joshieDo @rkrasiuk
crates/storage/storage-api/ @joshieDo
crates/tasks/ @mattsse
crates/tokio-util/ @fgimenez
crates/transaction-pool/ @mattsse
crates/trie/ @rkrasiuk @Rjected @shekhirin @mediocregopher
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

@@ -11,6 +11,7 @@ exclude_crates=(
# The following require investigation if they can be fixed
reth-basic-payload-builder
reth-bench
reth-bench-compare
reth-cli
reth-cli-commands
reth-cli-runner
@@ -68,6 +69,7 @@ exclude_crates=(
reth-payload-builder # reth-metrics
reth-provider # tokio
reth-prune # tokio
reth-prune-static-files # reth-provider
reth-stages-api # reth-provider, reth-prune
reth-static-file # tokio
reth-transaction-pool # c-kzg

View File

@@ -11,7 +11,8 @@ go build .
# Run each hive command in the background for each simulator and wait
echo "Building images"
./hive -client reth --sim "ethereum/eest" --sim.buildarg fixtures=https://github.com/ethereum/execution-spec-tests/releases/download/v4.4.0/fixtures_develop.tar.gz --sim.buildarg branch=v4.4.0 -sim.timelimit 1s || true &
# TODO: test code has been moved from https://github.com/ethereum/execution-spec-tests to https://github.com/ethereum/execution-specs we need to pin eels branch with `--sim.buildarg branch=<release-branch-name>` once we have the fusaka release tagged on the new repo
./hive -client reth --sim "ethereum/eels" --sim.buildarg fixtures=https://github.com/ethereum/execution-spec-tests/releases/download/v5.3.0/fixtures_develop.tar.gz -sim.timelimit 1s || true &
./hive -client reth --sim "ethereum/engine" -sim.timelimit 1s || true &
./hive -client reth --sim "devp2p" -sim.timelimit 1s || true &
./hive -client reth --sim "ethereum/rpc-compat" -sim.timelimit 1s || true &
@@ -27,8 +28,8 @@ docker save hive/hiveproxy:latest -o ../hive_assets/hiveproxy.tar & saving_pids+
docker save hive/simulators/devp2p:latest -o ../hive_assets/devp2p.tar & saving_pids+=( $! )
docker save hive/simulators/ethereum/engine:latest -o ../hive_assets/engine.tar & saving_pids+=( $! )
docker save hive/simulators/ethereum/rpc-compat:latest -o ../hive_assets/rpc_compat.tar & saving_pids+=( $! )
docker save hive/simulators/ethereum/eest/consume-engine:latest -o ../hive_assets/eest_engine.tar & saving_pids+=( $! )
docker save hive/simulators/ethereum/eest/consume-rlp:latest -o ../hive_assets/eest_rlp.tar & saving_pids+=( $! )
docker save hive/simulators/ethereum/eels/consume-engine:latest -o ../hive_assets/eels_engine.tar & saving_pids+=( $! )
docker save hive/simulators/ethereum/eels/consume-rlp:latest -o ../hive_assets/eels_rlp.tar & saving_pids+=( $! )
docker save hive/simulators/smoke/genesis:latest -o ../hive_assets/smoke_genesis.tar & saving_pids+=( $! )
docker save hive/simulators/smoke/network:latest -o ../hive_assets/smoke_network.tar & saving_pids+=( $! )
docker save hive/simulators/ethereum/sync:latest -o ../hive_assets/ethereum_sync.tar & saving_pids+=( $! )

View File

@@ -28,33 +28,46 @@ engine-withdrawals:
- Withdraw zero amount (Paris) (reth)
- Empty Withdrawals (Paris) (reth)
- Corrupted Block Hash Payload (INVALID) (Paris) (reth)
- Withdrawals Fork on Block 1 - 8 Block Re-Org NewPayload (Paris) (reth)
- Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org (Paris) (reth)
engine-api: []
engine-api: [ ]
# no fix due to https://github.com/paradigmxyz/reth/issues/8732
engine-cancun:
- Invalid PayloadAttributes, Missing BeaconRoot, Syncing=True (Cancun) (reth)
# the test fails with older verions of the code for which it passed before, probably related to changes
# the test fails with older versions of the code for which it passed before, probably related to changes
# in hive or its dependencies
- Blob Transaction Ordering, Multiple Clients (Cancun) (reth)
sync: []
sync: [ ]
# https://github.com/ethereum/hive/issues/1277
engine-auth:
- "JWT Authentication: No time drift, correct secret (Paris) (reth)"
- "JWT Authentication: Negative time drift, within limit, correct secret (Paris) (reth)"
- "JWT Authentication: Positive time drift, within limit, correct secret (Paris) (reth)"
engine-auth: [ ]
# 7702 test - no fix: its too expensive to check whether the storage is empty on each creation
# 6110 related tests - may start passing when fixtures improve
# 7002 related tests - post-fork test, should fix for spec compliance but not
# realistic on mainnet
# 7251 related tests - modified contract, not necessarily practical on mainnet,
# worth re-visiting when more of these related tests are passing
eest/consume-engine:
# EIP-7610 related tests (Revert creation in case of non-empty storage):
#
# tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_to_non_empty_storage
# The test artificially creates an empty account with storage, then tests EIP-7610's behavior.
# On mainnet, ~25 such accounts exist as contract addresses (derived from keccak(prefix, caller,
# nonce/salt), not from public keys). No private key exists for contract addresses. To trigger
# this with EIP-7702, you'd need to recover a private key from one of the already deployed contract addresses - mathematically impossible.
#
# tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_*
# Requires hash collision on create2 address to target already deployed accounts with storage.
# ~20-30 such accounts exist from before the state-clear EIP. Creating new accounts targeting
# these requires hash collision - mathematically impossible to trigger on mainnet.
# ref: https://github.com/ethereum/go-ethereum/pull/28666#issuecomment-1891997143
#
# System contract tests (already fixed and deployed):
#
# tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout and test_invalid_log_length
# System contract is already fixed and deployed; tests cover scenarios where contract is
# malformed which can't happen retroactively. No point in adding checks.
#
# tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment
# tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment
# Post-fork system contract deployment tests. Should fix for spec compliance but not realistic
# on mainnet as these contracts are already deployed at the correct addresses.
eels/consume-engine:
- tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_to_non_empty_storage[fork_Prague-blockchain_test_engine-zero_nonce]-reth
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-nonzero_balance]-reth
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-zero_balance]-reth
@@ -72,11 +85,76 @@ eest/consume-engine:
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-zero_balance]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Prague-blockchain_test_engine-slice_bytes_False]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Prague-blockchain_test_engine-slice_bytes_True]-reth
# the next test expects a concrete new format in the error message, there is no spec for this message, so it is ok to ignore
- tests/cancun/eip4844_blobs/test_blob_txs.py::test_blob_type_tx_pre_fork[fork_ShanghaiToCancunAtTime15k-blockchain_test_engine_from_state_test-one_blob_tx]-reth
# 7702 test - no fix: its too expensive to check whether the storage is empty on each creation
# rest of tests - see above
eest/consume-rlp:
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_amount_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_amount_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_index_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_index_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_pubkey_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_pubkey_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_signature_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_signature_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_withdrawal_credentials_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_withdrawal_credentials_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Osaka-blockchain_test_engine-slice_bytes_False]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Osaka-blockchain_test_engine-slice_bytes_True]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_engine_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_engine_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_1-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_2-blockchain_test_engine_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_0-blockchain_test_engine_from_state_test-non-empty-balance-correct-initcode]-reth
# Blob limit tests:
#
# tests/osaka/eip7594_peerdas/test_max_blob_per_tx.py::test_max_blobs_per_tx_fork_transition[fork_PragueToOsakaAtTime15k-blob_count_7-blockchain_test]
# this test inserts a chain via chain.rlp where the last block is invalid, but expects import to stop there, this doesn't work properly with our pipeline import approach hence the import fails when the invalid block is detected.
#. In other words, if this test fails, this means we're correctly rejecting the block.
#. The same test exists in the consume-engine simulator where it is passing as expected
eels/consume-rlp:
- tests/prague/eip7702_set_code_tx/test_set_code_txs.py::test_set_code_to_non_empty_storage[fork_Prague-blockchain_test-zero_nonce]-reth
- tests/prague/eip7251_consolidations/test_modified_consolidation_contract.py::test_system_contract_errors[fork_Prague-blockchain_test_engine-system_contract_reaches_gas_limit-system_contract_0x0000bbddc7ce488642fb579f8b00f3a590007251]-reth
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-nonzero_balance]-reth
@@ -92,11 +170,74 @@ eest/consume-rlp:
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_signature_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_withdrawal_credentials_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Prague-blockchain_test_engine-log_argument_withdrawal_credentials_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_amount_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_amount_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_index_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_index_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_pubkey_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_pubkey_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_signature_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_signature_size-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_withdrawal_credentials_offset-value_zero]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_layout[fork_Osaka-blockchain_test_engine-log_argument_withdrawal_credentials_size-value_zero]-reth
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-nonzero_balance]-reth
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test_engine-deploy_after_fork-zero_balance]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Prague-blockchain_test_engine-slice_bytes_False]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Prague-blockchain_test_engine-slice_bytes_True]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Osaka-blockchain_test_engine-slice_bytes_False]-reth
- tests/prague/eip6110_deposits/test_modified_contract.py::test_invalid_log_length[fork_Osaka-blockchain_test_engine-slice_bytes_True]-reth
- tests/osaka/eip7594_peerdas/test_max_blob_per_tx.py::test_max_blobs_per_tx_fork_transition[fork_PragueToOsakaAtTime15k-blob_count_7-blockchain_test]-reth
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test-deploy_after_fork-nonzero_balance]-reth
- tests/prague/eip7251_consolidations/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test-deploy_after_fork-zero_balance]-reth
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test-deploy_after_fork-nonzero_balance]-reth
- tests/prague/eip7002_el_triggerable_withdrawals/test_contract_deployment.py::test_system_contract_deployment[fork_CancunToPragueAtTime15k-blockchain_test-deploy_after_fork-zero_balance]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Cancun-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Osaka-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Paris-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Prague-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_from_state_test-opcode_CREATE-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_opcode[fork_Shanghai-blockchain_test_from_state_test-opcode_CREATE2-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_1-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Osaka-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Paris-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_0-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Cancun-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_2-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_1-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Prague-tx_type_2-blockchain_test_from_state_test-non-empty-balance-revert-initcode]-reth
- tests/paris/eip7610_create_collision/test_initcollision.py::test_init_collision_create_tx[fork_Shanghai-tx_type_0-blockchain_test_from_state_test-non-empty-balance-correct-initcode]-reth

View File

@@ -11,7 +11,26 @@
#
# When a test should no longer be ignored, remove it from this list.
# flaky
engine-withdrawals:
# flaky
- Withdrawals Fork on Block 1 - 8 Block Re-Org NewPayload (Paris) (reth)
- Withdrawals Fork on Block 8 - 10 Block Re-Org NewPayload (Paris) (reth)
- Withdrawals Fork on Canonical Block 8 / Side Block 7 - 10 Block Re-Org (Paris) (reth)
- Sync after 128 blocks - Withdrawals on Block 2 - Multiple Withdrawal Accounts (Paris) (reth)
engine-cancun:
- Transaction Re-Org, New Payload on Revert Back (Cancun) (reth)
- Transaction Re-Org, Re-Org to Different Block (Cancun) (reth)
- Transaction Re-Org, Re-Org Out (Cancun) (reth)
- Invalid Missing Ancestor ReOrg, StateRoot, EmptyTxs=False, Invalid P9 (Cancun) (reth)
- Multiple New Payloads Extending Canonical Chain, Wait for Canonical Payload (Cancun) (reth)
engine-api:
- Transaction Re-Org, Re-Org Out (Paris) (reth)
- Transaction Re-Org, Re-Org to Different Block (Paris) (reth)
- Transaction Re-Org, New Payload on Revert Back (Paris) (reth)
- Transaction Re-Org, Re-Org to Different Block (Paris) (reth)
- Invalid Missing Ancestor Syncing ReOrg, Transaction Nonce, EmptyTxs=False, CanonicalReOrg=False, Invalid P9 (Paris) (reth)
- Invalid Missing Ancestor Syncing ReOrg, Transaction Signature, EmptyTxs=False, CanonicalReOrg=True, Invalid P9 (Paris) (reth)
- Invalid Missing Ancestor Syncing ReOrg, Transaction Signature, EmptyTxs=False, CanonicalReOrg=False, Invalid P9 (Paris) (reth)
- Invalid Missing Ancestor ReOrg, StateRoot, EmptyTxs=True, Invalid P10 (Paris) (reth)
- Multiple New Payloads Extending Canonical Chain, Wait for Canonical Payload (Paris) (reth)
- Multiple New Payloads Extending Canonical Chain, Set Head to First Payload Received (Paris) (reth)

View File

@@ -11,8 +11,8 @@ IMAGES=(
"/tmp/smoke_genesis.tar"
"/tmp/smoke_network.tar"
"/tmp/ethereum_sync.tar"
"/tmp/eest_engine.tar"
"/tmp/eest_rlp.tar"
"/tmp/eels_engine.tar"
"/tmp/eels_rlp.tar"
"/tmp/reth_image.tar"
)

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

@@ -18,12 +18,19 @@ ethereum_package:
}'
optimism_package:
chains:
- participants:
- el_type: op-geth
cl_type: op-node
- el_type: op-reth
cl_type: op-node
el_image: "ghcr.io/paradigmxyz/op-reth:kurtosis-ci"
chain0:
participants:
node0:
el:
type: op-geth
cl:
type: op-node
node1:
el:
type: op-reth
image: "ghcr.io/paradigmxyz/op-reth:kurtosis-ci"
cl:
type: op-node
network_params:
holocene_time_offset: 0
isthmus_time_offset: 0

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
@@ -33,7 +34,8 @@ jobs:
- name: Build the benchmark target(s)
run: ./.github/scripts/codspeed-build.sh
- name: Run the benchmarks
uses: CodSpeedHQ/action@v3
uses: CodSpeedHQ/action@v4
with:
run: cargo codspeed run --workspace
mode: instrumentation
token: ${{ secrets.CODSPEED_TOKEN }}

View File

@@ -10,16 +10,21 @@ on:
types: [opened, reopened, synchronize, closed]
merge_group:
env:
RUSTC_WRAPPER: "sccache"
jobs:
build:
runs-on: ubuntu-latest
timeout-minutes: 60
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
with:
bun-version: v1.2.23
- name: Install Playwright browsers
# Required for rehype-mermaid to render Mermaid diagrams during build
@@ -31,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,31 +24,55 @@ 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
- uses: actions/setup-go@v5
- name: Get hive commit hash
id: hive-commit
run: echo "hash=$(cd hivetests && git rev-parse HEAD)" >> $GITHUB_OUTPUT
- uses: actions/setup-go@v6
with:
go-version: "^1.13.1"
- run: go version
- name: Restore hive assets cache
id: cache-hive
uses: actions/cache@v5
with:
path: ./hive_assets
key: hive-assets-${{ steps.hive-commit.outputs.hash }}-${{ hashFiles('.github/assets/hive/build_simulators.sh') }}
- name: Build hive assets
if: steps.cache-hive.outputs.cache-hit != 'true'
run: .github/assets/hive/build_simulators.sh
- name: Load cached Docker images
if: steps.cache-hive.outputs.cache-hit == 'true'
run: |
cd hive_assets
for tar_file in *.tar; do
if [ -f "$tar_file" ]; then
echo "Loading $tar_file..."
docker load -i "$tar_file"
fi
done
# Make hive binary executable
chmod +x hive
- name: Upload hive assets
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v6
with:
name: hive_assets
path: ./hive_assets
test:
timeout-minutes: 60
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
@@ -112,57 +136,64 @@ jobs:
- debug_
# consume-engine
- sim: ethereum/eest/consume-engine
- sim: ethereum/eels/consume-engine
limit: .*tests/osaka.*
- sim: ethereum/eels/consume-engine
limit: .*tests/prague.*
- sim: ethereum/eest/consume-engine
- sim: ethereum/eels/consume-engine
limit: .*tests/cancun.*
- sim: ethereum/eest/consume-engine
- sim: ethereum/eels/consume-engine
limit: .*tests/shanghai.*
- sim: ethereum/eest/consume-engine
- sim: ethereum/eels/consume-engine
limit: .*tests/berlin.*
- sim: ethereum/eest/consume-engine
- sim: ethereum/eels/consume-engine
limit: .*tests/istanbul.*
- sim: ethereum/eest/consume-engine
- sim: ethereum/eels/consume-engine
limit: .*tests/homestead.*
- sim: ethereum/eest/consume-engine
- sim: ethereum/eels/consume-engine
limit: .*tests/frontier.*
- sim: ethereum/eels/consume-engine
limit: .*tests/paris.*
# consume-rlp
- sim: ethereum/eest/consume-rlp
- sim: ethereum/eels/consume-rlp
limit: .*tests/osaka.*
- sim: ethereum/eels/consume-rlp
limit: .*tests/prague.*
- sim: ethereum/eest/consume-rlp
- sim: ethereum/eels/consume-rlp
limit: .*tests/cancun.*
- sim: ethereum/eest/consume-rlp
- sim: ethereum/eels/consume-rlp
limit: .*tests/shanghai.*
- sim: ethereum/eest/consume-rlp
- sim: ethereum/eels/consume-rlp
limit: .*tests/berlin.*
- sim: ethereum/eest/consume-rlp
- sim: ethereum/eels/consume-rlp
limit: .*tests/istanbul.*
- sim: ethereum/eest/consume-rlp
- sim: ethereum/eels/consume-rlp
limit: .*tests/homestead.*
- sim: ethereum/eest/consume-rlp
- sim: ethereum/eels/consume-rlp
limit: .*tests/frontier.*
- sim: ethereum/eels/consume-rlp
limit: .*tests/paris.*
needs:
- 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@v5
uses: actions/download-artifact@v7
with:
name: hive_assets
path: /tmp
- name: Download reth image
uses: actions/download-artifact@v5
uses: actions/download-artifact@v7
with:
name: artifacts
path: /tmp
@@ -176,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
@@ -214,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@v5
uses: actions/download-artifact@v7
with:
name: artifacts
path: /tmp
@@ -62,12 +61,10 @@ jobs:
sudo apt update
sudo apt install kurtosis-cli
kurtosis engine start
# TODO: unpin optimism-package when https://github.com/ethpandaops/optimism-package/issues/340 is fixed
# kurtosis run --enclave op-devnet github.com/ethpandaops/optimism-package --args-file .github/assets/kurtosis_op_network_params.yaml
kurtosis run --enclave op-devnet github.com/ethpandaops/optimism-package@452133367b693e3ba22214a6615c86c60a1efd5e --args-file .github/assets/kurtosis_op_network_params.yaml
kurtosis run --enclave op-devnet github.com/ethpandaops/optimism-package --args-file .github/assets/kurtosis_op_network_params.yaml
ENCLAVE_ID=$(curl http://127.0.0.1:9779/api/enclaves | jq --raw-output 'keys[0]')
GETH_PORT=$(curl "http://127.0.0.1:9779/api/enclaves/$ENCLAVE_ID/services" | jq '."op-el-2151908-1-op-geth-op-node-op-kurtosis".public_ports.rpc.number')
RETH_PORT=$(curl "http://127.0.0.1:9779/api/enclaves/$ENCLAVE_ID/services" | jq '."op-el-2151908-2-op-reth-op-node-op-kurtosis".public_ports.rpc.number')
GETH_PORT=$(curl "http://127.0.0.1:9779/api/enclaves/$ENCLAVE_ID/services" | jq '."op-el-2151908-node0-op-geth".public_ports.rpc.number')
RETH_PORT=$(curl "http://127.0.0.1:9779/api/enclaves/$ENCLAVE_ID/services" | jq '."op-el-2151908-node1-op-reth".public_ports.rpc.number')
echo "GETH_RPC=http://127.0.0.1:$GETH_PORT" >> $GITHUB_ENV
echo "RETH_RPC=http://127.0.0.1:$RETH_PORT" >> $GITHUB_ENV
@@ -85,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@v5
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,12 +11,12 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Label PRs
uses: actions/github-script@v7
uses: actions/github-script@v8
with:
script: |
const label_pr = require('./.github/assets/label_pr.js')

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,45 +237,52 @@ 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
- name: fetch deps
run: |
# Eagerly pull dependencies
time cargo metadata --format-version=1 --locked > /dev/null
- name: run zepter
run: |
cargo install zepter -f --locked
zepter --version
time zepter run check
- 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:
tool: zepter
- name: Eagerly pull dependencies
run: cargo metadata --format-version=1 --locked > /dev/null
- run: zepter run check
deny:
uses: ithacaxyz/ci/.github/workflows/deny.yml@main

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@v4
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
@@ -78,7 +80,7 @@ jobs:
profile: maxperf
allow_fail: false
- target: x86_64-apple-darwin
os: macos-13
os: macos-14
profile: maxperf
allow_fail: false
- target: aarch64-apple-darwin
@@ -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@v4
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@v4
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@v5
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

@@ -14,7 +14,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v9
- uses: actions/stale@v10
with:
days-before-stale: 21
days-before-close: 7

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,25 +66,34 @@ 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
path: testing/ef-tests/ethereum-tests
submodules: recursive
fetch-depth: 1
- name: Download & extract EEST fixtures (public)
shell: bash
env:
EEST_TESTS_TAG: v4.5.0
run: |
set -euo pipefail
mkdir -p testing/ef-tests/execution-spec-tests
URL="https://github.com/ethereum/execution-spec-tests/releases/download/${EEST_TESTS_TAG}/fixtures_stable.tar.gz"
curl -L "$URL" | tar -xz --strip-components=1 -C testing/ef-tests/execution-spec-tests
- 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
@@ -91,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

View File

@@ -236,6 +236,85 @@ Common refactoring pattern:
- Add trait bounds for flexibility
- Enable reuse across different chain types (Ethereum, Optimism)
#### When to Comment
Write comments that remain valuable after the PR is merged. Future readers won't have PR context - they only see the current code.
##### ✅ DO: Add Value
**Explain WHY and non-obvious behavior:**
```rust
// Process must handle allocations atomically to prevent race conditions
// between dealloc on drop and concurrent limit checks
unsafe impl GlobalAlloc for LimitedAllocator { ... }
// Binary search requires sorted input. Panics on unsorted slices.
fn find_index(items: &[Item], target: &Item) -> Option
// Timeout set to 5s to match EVM block processing limits
const TRACER_TIMEOUT: Duration = Duration::from_secs(5);
```
**Document constraints and assumptions:**
```rust
/// Returns heap size estimate.
///
/// Note: May undercount shared references (Rc/Arc). For precise
/// accounting, combine with an allocator-based approach.
fn deep_size_of(&self) -> usize
```
**Explain complex logic:**
```rust
// We reset limits at task start because tokio reuses threads in
// spawn_blocking pool. Without reset, second task inherits first
// task's allocation count and immediately hits limit.
THREAD_ALLOCATED.with(|allocated| allocated.set(0));
```
##### ❌ DON'T: Describe Changes
```rust
// ❌ BAD - Describes the change, not the code
// Changed from Vec to HashMap for O(1) lookups
// ✅ GOOD - Explains the decision
// HashMap provides O(1) symbol lookups during trace replay
```
```rust
// ❌ BAD - PR-specific context
// Fix for issue #234 where memory wasn't freed
// ✅ GOOD - Documents the actual behavior
// Explicitly drop allocations before limit check to ensure
// accurate accounting
```
```rust
// ❌ BAD - States the obvious
// Increment counter
counter += 1;
// ✅ GOOD - Explains non-obvious purpose
// Track allocations across all threads for global limit enforcement
GLOBAL_COUNTER.fetch_add(1, Ordering::SeqCst);
```
✅ **Comment when:**
- Non-obvious behavior or edge cases
- Performance trade-offs
- Safety requirements (unsafe blocks must always be documented)
- Limitations or gotchas
- Why simpler alternatives don't work
❌ **Don't comment when:**
- Code is self-explanatory
- Just restating the code in English
- Describing what changed in this PR
##### The Test: "Will this make sense in 6 months?"
Before adding a comment, ask: Would someone reading just the current code (no PR, no history) find this helpful?
### Example Contribution Workflow
Let's say you want to fix a bug where external IP resolution fails on startup:

View File

@@ -3,7 +3,7 @@
Thanks for your interest in improving Reth!
There are multiple opportunities to contribute at any level. It doesn't matter if you are just getting started with Rust
or are the most weathered expert, we can use your help.
or if you are already the most weathered expert, we can use your help.
**No contribution is too small and all contributions are valued.**

3453
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
[workspace.package]
version = "1.7.0"
edition = "2021"
version = "1.9.3"
edition = "2024"
rust-version = "1.88"
license = "MIT OR Apache-2.0"
homepage = "https://paradigmxyz.github.io/reth"
@@ -10,6 +10,7 @@ exclude = [".github/"]
[workspace]
members = [
"bin/reth-bench/",
"bin/reth-bench-compare/",
"bin/reth/",
"crates/storage/rpc-provider/",
"crates/chain-state/",
@@ -93,6 +94,7 @@ members = [
"crates/payload/util/",
"crates/primitives-traits/",
"crates/primitives/",
"crates/prune/db",
"crates/prune/prune",
"crates/prune/types",
"crates/ress/protocol",
@@ -146,10 +148,12 @@ members = [
"examples/custom-node/",
"examples/custom-engine-types/",
"examples/custom-evm/",
"examples/custom-hardforks/",
"examples/custom-inspector/",
"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",
@@ -172,6 +176,7 @@ members = [
"examples/custom-beacon-withdrawals",
"testing/ef-tests/",
"testing/testing-utils",
"testing/runner",
"crates/tracing-otlp",
]
default-members = ["bin/reth"]
@@ -187,6 +192,7 @@ rust.missing_docs = "warn"
rust.rust_2018_idioms = { level = "deny", priority = -1 }
rust.unreachable_pub = "warn"
rust.unused_must_use = "deny"
rust.rust_2024_incompatible_pat = "warn"
rustdoc.all = "warn"
# rust.unnameable-types = "warn"
@@ -323,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" }
@@ -330,6 +342,7 @@ reth = { path = "bin/reth" }
reth-storage-rpc-provider = { path = "crates/storage/rpc-provider" }
reth-basic-payload-builder = { path = "crates/payload/basic" }
reth-bench = { path = "bin/reth-bench" }
reth-bench-compare = { path = "bin/reth-bench-compare" }
reth-chain-state = { path = "crates/chain-state" }
reth-chainspec = { path = "crates/chainspec", default-features = false }
reth-cli = { path = "crates/cli/cli" }
@@ -363,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-cli = { path = "crates/ethereum/cli" }
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" }
@@ -404,7 +417,7 @@ reth-optimism-node = { path = "crates/optimism/node" }
reth-node-types = { path = "crates/node/types" }
reth-op = { path = "crates/optimism/reth", default-features = false }
reth-optimism-chainspec = { path = "crates/optimism/chainspec", default-features = false }
reth-optimism-cli = { path = "crates/optimism/cli" }
reth-optimism-cli = { path = "crates/optimism/cli", default-features = false }
reth-optimism-consensus = { path = "crates/optimism/consensus", default-features = false }
reth-optimism-forks = { path = "crates/optimism/hardforks", default-features = false }
reth-optimism-payload-builder = { path = "crates/optimism/payload" }
@@ -438,7 +451,7 @@ reth-rpc-convert = { path = "crates/rpc/rpc-convert" }
reth-stages = { path = "crates/stages/stages" }
reth-stages-api = { path = "crates/stages/api" }
reth-stages-types = { path = "crates/stages/types", default-features = false }
reth-stateless = { path = "crates/stateless" }
reth-stateless = { path = "crates/stateless", default-features = false }
reth-static-file = { path = "crates/static-file/static-file" }
reth-static-file-types = { path = "crates/static-file/types", default-features = false }
reth-storage-api = { path = "crates/storage/storage-api", default-features = false }
@@ -446,7 +459,8 @@ reth-storage-errors = { path = "crates/storage/errors", default-features = false
reth-tasks = { path = "crates/tasks" }
reth-testing-utils = { path = "testing/testing-utils" }
reth-tokio-util = { path = "crates/tokio-util" }
reth-tracing = { path = "crates/tracing" }
reth-tracing = { path = "crates/tracing", default-features = false }
reth-tracing-otlp = { path = "crates/tracing-otlp" }
reth-transaction-pool = { path = "crates/transaction-pool" }
reth-trie = { path = "crates/trie/trie" }
reth-trie-common = { path = "crates/trie/common", default-features = false }
@@ -459,72 +473,71 @@ reth-ress-protocol = { path = "crates/ress/protocol" }
reth-ress-provider = { path = "crates/ress/provider" }
# revm
revm = { version = "29.0.0", default-features = false }
revm-bytecode = { version = "6.2.2", default-features = false }
revm-database = { version = "7.0.5", default-features = false }
revm-state = { version = "7.0.5", default-features = false }
revm-primitives = { version = "20.2.1", default-features = false }
revm-interpreter = { version = "25.0.2", default-features = false }
revm-inspector = { version = "10.0.0", default-features = false }
revm-context = { version = "9.0.2", default-features = false }
revm-context-interface = { version = "10.1.0", default-features = false }
revm-database-interface = { version = "7.0.5", default-features = false }
op-revm = { version = "10.0.0", default-features = false }
revm-inspectors = "0.29.0"
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 = "31.1.0", default-features = false }
revm-database-interface = { version = "8.0.5", default-features = false }
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.3.1"
alloy-dyn-abi = "1.4.1"
alloy-eip2124 = { version = "0.2.0", default-features = false }
alloy-evm = { version = "0.20.1", default-features = false }
alloy-primitives = { version = "1.3.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.3.1"
alloy-sol-types = { version = "1.3.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.3.0"
alloy-hardforks = "0.4.5"
alloy-consensus = { version = "1.0.30", default-features = false }
alloy-contract = { version = "1.0.30", default-features = false }
alloy-eips = { version = "1.0.30", default-features = false }
alloy-genesis = { version = "1.0.30", default-features = false }
alloy-json-rpc = { version = "1.0.30", default-features = false }
alloy-network = { version = "1.0.30", default-features = false }
alloy-network-primitives = { version = "1.0.30", default-features = false }
alloy-provider = { version = "1.0.30", features = ["reqwest"], default-features = false }
alloy-pubsub = { version = "1.0.30", default-features = false }
alloy-rpc-client = { version = "1.0.30", default-features = false }
alloy-rpc-types = { version = "1.0.30", features = ["eth"], default-features = false }
alloy-rpc-types-admin = { version = "1.0.30", default-features = false }
alloy-rpc-types-anvil = { version = "1.0.30", default-features = false }
alloy-rpc-types-beacon = { version = "1.0.30", default-features = false }
alloy-rpc-types-debug = { version = "1.0.30", default-features = false }
alloy-rpc-types-engine = { version = "1.0.30", default-features = false }
alloy-rpc-types-eth = { version = "1.0.30", default-features = false }
alloy-rpc-types-mev = { version = "1.0.30", default-features = false }
alloy-rpc-types-trace = { version = "1.0.30", default-features = false }
alloy-rpc-types-txpool = { version = "1.0.30", default-features = false }
alloy-serde = { version = "1.0.30", default-features = false }
alloy-signer = { version = "1.0.30", default-features = false }
alloy-signer-local = { version = "1.0.30", default-features = false }
alloy-transport = { version = "1.0.30" }
alloy-transport-http = { version = "1.0.30", features = ["reqwest-rustls-tls"], default-features = false }
alloy-transport-ipc = { version = "1.0.30", default-features = false }
alloy-transport-ws = { version = "1.0.30", 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.20.1", default-features = false }
alloy-op-hardforks = "0.3.0"
op-alloy-rpc-types = { version = "0.19.0", default-features = false }
op-alloy-rpc-types-engine = { version = "0.19.0", default-features = false }
op-alloy-network = { version = "0.19.0", default-features = false }
op-alloy-consensus = { version = "0.19.0", default-features = false }
op-alloy-rpc-jsonrpsee = { version = "0.19.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.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
either = { version = "1.15.0", default-features = false }
arrayvec = { version = "0.7.6", default-features = false }
aquamarine = "0.6"
auto_impl = "1"
backon = { version = "1.2", default-features = false, features = ["std-blocking-sleep", "tokio-sleep"] }
@@ -541,7 +554,6 @@ dirs-next = "2.0.0"
dyn-clone = "1.0.17"
eyre = "0.6"
fdlimit = "0.3.0"
generic-array = "0.14"
humantime = "2.1"
humantime-serde = "1.1"
itertools = { version = "0.14", default-features = false }
@@ -562,6 +574,7 @@ serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
serde_with = { version = "3", default-features = false, features = ["macros"] }
sha2 = { version = "0.10", default-features = false }
shellexpand = "3.0.0"
shlex = "1.3"
smallvec = "1"
strum = { version = "0.27", default-features = false }
strum_macros = "0.27"
@@ -596,6 +609,7 @@ tokio-tungstenite = "0.26.2"
tokio-util = { version = "0.7.4", features = ["codec"] }
# async
async-compression = { version = "0.4", default-features = false }
async-stream = "0.3"
async-trait = "0.1.68"
futures = "0.3"
@@ -610,8 +624,8 @@ tower = "0.5"
tower-http = "0.6"
# p2p
discv5 = "0.9"
if-addrs = "0.13"
discv5 = "0.10"
if-addrs = "0.14"
# rpc
jsonrpsee = "0.26.0"
@@ -635,15 +649,26 @@ secp256k1 = { version = "0.30", default-features = false, features = ["global-co
rand_08 = { package = "rand", version = "0.8" }
# for eip-4844
c-kzg = "2.1.1"
c-kzg = "2.1.5"
# config
toml = "0.8"
# rocksdb
rocksdb = { version = "0.24" }
# otlp obs
opentelemetry_sdk = "0.31"
opentelemetry = "0.31"
opentelemetry-otlp = "0.31"
opentelemetry-semantic-conventions = "0.31"
tracing-opentelemetry = "0.32"
# misc-testing
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"] }
@@ -665,16 +690,16 @@ snmalloc-rs = { version = "0.3.7", features = ["build_cc"] }
aes = "0.8.1"
ahash = "0.8"
anyhow = "1.0"
bindgen = { version = "0.70", default-features = false }
bindgen = { version = "0.71", default-features = false }
block-padding = "0.3.2"
cc = "=1.2.15"
cipher = "0.4.3"
comfy-table = "7.0"
concat-kdf = "0.1.0"
convert_case = "0.7.0"
crossbeam-channel = "0.5.13"
crossterm = "0.28.0"
csv = "1.3.0"
ctrlc = "3.4"
ctr = "0.9.2"
data-encoding = "2"
delegate = "0.13"
@@ -712,6 +737,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" }
@@ -754,3 +782,6 @@ vergen-git2 = "1.0.5"
# jsonrpsee-server = { git = "https://github.com/paradigmxyz/jsonrpsee", branch = "matt/make-rpc-service-pub" }
# jsonrpsee-http-client = { git = "https://github.com/paradigmxyz/jsonrpsee", branch = "matt/make-rpc-service-pub" }
# jsonrpsee-types = { git = "https://github.com/paradigmxyz/jsonrpsee", branch = "matt/make-rpc-service-pub" }
# alloy-evm = { git = "https://github.com/alloy-rs/evm", rev = "a69f0b45a6b0286e16072cb8399e02ce6ceca353" }
# alloy-op-evm = { git = "https://github.com/alloy-rs/evm", rev = "a69f0b45a6b0286e16072cb8399e02ce6ceca353" }

View File

@@ -7,7 +7,7 @@ LABEL org.opencontainers.image.source=https://github.com/paradigmxyz/reth
LABEL org.opencontainers.image.licenses="MIT OR Apache-2.0"
# Install system dependencies
RUN apt-get update && apt-get -y upgrade && apt-get install -y libclang-dev pkg-config
RUN apt-get update && apt-get install -y libclang-dev pkg-config
# Builds a cargo-chef plan
FROM chef AS planner

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

@@ -6,13 +6,13 @@ LABEL org.opencontainers.image.licenses="MIT OR Apache-2.0"
RUN apt-get update && apt-get -y upgrade && apt-get install -y libclang-dev pkg-config
# Builds a cargo-chef plan
FROM chef AS planner
COPY . .
RUN cargo chef prepare --recipe-path recipe.json
FROM chef AS builder
COPY --from=planner /app/recipe.json recipe.json
COPY . .
ARG BUILD_PROFILE=release
ENV BUILD_PROFILE=$BUILD_PROFILE
@@ -20,15 +20,18 @@ ENV BUILD_PROFILE=$BUILD_PROFILE
ARG RUSTFLAGS=""
ENV RUSTFLAGS="$RUSTFLAGS"
RUN cargo chef cook --profile $BUILD_PROFILE --recipe-path recipe.json --manifest-path /app/crates/optimism/bin/Cargo.toml
ARG FEATURES=""
ENV FEATURES=$FEATURES
RUN cargo chef cook --profile $BUILD_PROFILE --features "$FEATURES" --recipe-path recipe.json --manifest-path /app/crates/optimism/bin/Cargo.toml
COPY . .
RUN cargo build --profile $BUILD_PROFILE --bin op-reth --manifest-path /app/crates/optimism/bin/Cargo.toml
RUN cargo build --profile $BUILD_PROFILE --features "$FEATURES" --bin op-reth --manifest-path /app/crates/optimism/bin/Cargo.toml
RUN ls -la /app/target/$BUILD_PROFILE/op-reth
RUN cp /app/target/$BUILD_PROFILE/op-reth /app/op-reth
FROM ubuntu:22.04 AS runtime
FROM ubuntu AS runtime
RUN apt-get update && \
apt-get install -y ca-certificates libssl-dev pkg-config strace && \

View File

@@ -30,6 +30,11 @@ EF_TESTS_TAG := v17.0
EF_TESTS_URL := https://github.com/ethereum/tests/archive/refs/tags/$(EF_TESTS_TAG).tar.gz
EF_TESTS_DIR := ./testing/ef-tests/ethereum-tests
# The release tag of https://github.com/ethereum/execution-spec-tests to use for EEST tests
EEST_TESTS_TAG := v4.5.0
EEST_TESTS_URL := https://github.com/ethereum/execution-spec-tests/releases/download/$(EEST_TESTS_TAG)/fixtures_stable.tar.gz
EEST_TESTS_DIR := ./testing/ef-tests/execution-spec-tests
# The docker image name
DOCKER_IMAGE_NAME ?= ghcr.io/paradigmxyz/reth
@@ -60,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.
@@ -150,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)
@@ -202,9 +217,18 @@ $(EF_TESTS_DIR):
tar -xzf ethereum-tests.tar.gz --strip-components=1 -C $(EF_TESTS_DIR)
rm ethereum-tests.tar.gz
# Downloads and unpacks EEST tests in the `$(EEST_TESTS_DIR)` directory.
#
# Requires `wget` and `tar`
$(EEST_TESTS_DIR):
mkdir $(EEST_TESTS_DIR)
wget $(EEST_TESTS_URL) -O execution-spec-tests.tar.gz
tar -xzf execution-spec-tests.tar.gz --strip-components=1 -C $(EEST_TESTS_DIR)
rm execution-spec-tests.tar.gz
.PHONY: ef-tests
ef-tests: $(EF_TESTS_DIR) ## Runs Ethereum Foundation tests.
cargo nextest run -p ef-tests --features ef-tests
ef-tests: $(EF_TESTS_DIR) $(EEST_TESTS_DIR) ## Runs Legacy and EEST tests.
cargo nextest run -p ef-tests --release --features ef-tests
##@ reth-bench
@@ -366,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.
@@ -497,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

@@ -20,7 +20,7 @@
## What is Reth?
Reth (short for Rust Ethereum, [pronunciation](https://twitter.com/kelvinfichter/status/1597653609411268608)) is a new Ethereum full node implementation that is focused on being user-friendly, highly modular, as well as being fast and efficient. Reth is an Execution Layer (EL) and is compatible with all Ethereum Consensus Layer (CL) implementations that support the [Engine API](https://github.com/ethereum/execution-apis/tree/a0d03086564ab1838b462befbc083f873dcf0c0f/src/engine). It is originally built and driven forward by [Paradigm](https://paradigm.xyz/), and is licensed under the Apache and MIT licenses.
Reth (short for Rust Ethereum, [pronunciation](https://x.com/kelvinfichter/status/1597653609411268608)) is a new Ethereum full node implementation that is focused on being user-friendly, highly modular, as well as being fast and efficient. Reth is an Execution Layer (EL) and is compatible with all Ethereum Consensus Layer (CL) implementations that support the [Engine API](https://github.com/ethereum/execution-apis/tree/a0d03086564ab1838b462befbc083f873dcf0c0f/src/engine). It is originally built and driven forward by [Paradigm](https://paradigm.xyz/), and is licensed under the Apache and MIT licenses.
## Goals
@@ -43,7 +43,7 @@ More historical context below:
- We released 1.0 "production-ready" stable Reth in June 2024.
- Reth completed an audit with [Sigma Prime](https://sigmaprime.io/), the developers of [Lighthouse](https://github.com/sigp/lighthouse), the Rust Consensus Layer implementation. Find it [here](./audit/sigma_prime_audit_v2.pdf).
- Revm (the EVM used in Reth) underwent an audit with [Guido Vranken](https://twitter.com/guidovranken) (#1 [Ethereum Bug Bounty](https://ethereum.org/en/bug-bounty)). We will publish the results soon.
- Revm (the EVM used in Reth) underwent an audit with [Guido Vranken](https://x.com/guidovranken) (#1 [Ethereum Bug Bounty](https://ethereum.org/en/bug-bounty)). We will publish the results soon.
- We released multiple iterative beta versions, up to [beta.9](https://github.com/paradigmxyz/reth/releases/tag/v0.2.0-beta.9) on Monday June 3, 2024,the last beta release.
- We released [beta](https://github.com/paradigmxyz/reth/releases/tag/v0.2.0-beta.1) on Monday March 4, 2024, our first breaking change to the database model, providing faster query speed, smaller database footprint, and allowing "history" to be mounted on separate drives.
- We shipped iterative improvements until the last alpha release on February 28, 2024, [0.1.0-alpha.21](https://github.com/paradigmxyz/reth/releases/tag/v0.1.0-alpha.21).
@@ -61,7 +61,7 @@ If you had a database produced by alpha versions of Reth, you need to drop it wi
## For Users
See the [Reth documentation](https://paradigmxyz.github.io/reth) for instructions on how to install and run Reth.
See the [Reth documentation](https://reth.rs/) for instructions on how to install and run Reth.
## For Developers
@@ -69,7 +69,7 @@ See the [Reth documentation](https://paradigmxyz.github.io/reth) for instruction
You can use individual crates of reth in your project.
The crate docs can be found [here](https://paradigmxyz.github.io/reth/docs).
The crate docs can be found [here](https://reth.rs/docs/).
For a general overview of the crates, see [Project Layout](./docs/repo/layout.md).
@@ -90,7 +90,7 @@ When updating this, also update:
The Minimum Supported Rust Version (MSRV) of this project is [1.88.0](https://blog.rust-lang.org/2025/06/26/Rust-1.88.0/).
See the docs for detailed instructions on how to [build from source](https://paradigmxyz.github.io/reth/installation/source).
See the docs for detailed instructions on how to [build from source](https://reth.rs/installation/source/).
To fully test Reth, you will need to have [Geth installed](https://geth.ethereum.org/docs/getting-started/installing-geth), but it is possible to run a subset of tests without Geth.
@@ -145,5 +145,5 @@ None of this would have been possible without them, so big shoutout to the teams
The `NippyJar` and `Compact` encoding formats and their implementations are designed for storing and retrieving data internally. They are not hardened to safely read potentially malicious data.
[book]: https://paradigmxyz.github.io/reth/
[book]: https://reth.rs/
[tg-url]: https://t.me/paradigm_reth

View File

@@ -0,0 +1,96 @@
[package]
name = "reth-bench-compare"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
license.workspace = true
homepage.workspace = true
repository.workspace = true
description = "Automated reth benchmark comparison between git references"
[lints]
workspace = true
[[bin]]
name = "reth-bench-compare"
path = "src/main.rs"
[dependencies]
# reth
reth-cli-runner.workspace = true
reth-cli-util.workspace = true
reth-node-core.workspace = true
reth-tracing.workspace = true
reth-chainspec.workspace = true
# alloy
alloy-provider = { workspace = true, features = ["reqwest-rustls-tls"], default-features = false }
alloy-rpc-types-eth.workspace = true
alloy-primitives.workspace = true
# CLI and argument parsing
clap = { workspace = true, features = ["derive", "env"] }
eyre.workspace = true
# Async runtime
tokio = { workspace = true, features = ["full"] }
tracing.workspace = true
# Serialization
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
# Time handling
chrono = { workspace = true, features = ["serde"] }
# Path manipulation
shellexpand.workspace = true
# CSV handling
csv.workspace = true
# Process management
ctrlc.workspace = true
shlex.workspace = true
[target.'cfg(unix)'.dependencies]
nix = { version = "0.29", features = ["signal", "process"] }
[features]
default = ["jemalloc"]
asm-keccak = [
"reth-node-core/asm-keccak",
"alloy-primitives/asm-keccak",
]
jemalloc = [
"reth-cli-util/jemalloc",
"reth-node-core/jemalloc",
]
jemalloc-prof = ["reth-cli-util/jemalloc-prof"]
tracy-allocator = ["reth-cli-util/tracy-allocator"]
min-error-logs = [
"tracing/release_max_level_error",
"reth-node-core/min-error-logs",
]
min-warn-logs = [
"tracing/release_max_level_warn",
"reth-node-core/min-warn-logs",
]
min-info-logs = [
"tracing/release_max_level_info",
"reth-node-core/min-info-logs",
]
min-debug-logs = [
"tracing/release_max_level_debug",
"reth-node-core/min-debug-logs",
]
min-trace-logs = [
"tracing/release_max_level_trace",
"reth-node-core/min-trace-logs",
]
# no-op feature flag for switching between the `optimism` and default functionality in CI matrices
ethereum = []

View File

@@ -0,0 +1,298 @@
//! Benchmark execution using reth-bench.
use crate::cli::Args;
use eyre::{eyre, Result, WrapErr};
use std::{
path::Path,
sync::{Arc, Mutex},
};
use tokio::{
fs::File as AsyncFile,
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
process::Command,
};
use tracing::{debug, error, info, warn};
/// Manages benchmark execution using reth-bench
pub(crate) struct BenchmarkRunner {
rpc_url: String,
jwt_secret: String,
wait_time: Option<String>,
warmup_blocks: u64,
}
impl BenchmarkRunner {
/// Create a new `BenchmarkRunner` from CLI arguments
pub(crate) fn new(args: &Args) -> Self {
Self {
rpc_url: args.get_rpc_url(),
jwt_secret: args.jwt_secret_path().to_string_lossy().to_string(),
wait_time: args.wait_time.clone(),
warmup_blocks: args.get_warmup_blocks(),
}
}
/// Clear filesystem caches (page cache, dentries, and inodes)
pub(crate) async fn clear_fs_caches() -> Result<()> {
info!("Clearing filesystem caches...");
// First sync to ensure all pending writes are flushed
let sync_output =
Command::new("sync").output().await.wrap_err("Failed to execute sync command")?;
if !sync_output.status.success() {
return Err(eyre!("sync command failed"));
}
// Drop caches - requires sudo/root permissions
// 3 = drop pagecache, dentries, and inodes
let drop_caches_cmd = Command::new("sudo")
.args(["-n", "sh", "-c", "echo 3 > /proc/sys/vm/drop_caches"])
.output()
.await;
match drop_caches_cmd {
Ok(output) if output.status.success() => {
info!("Successfully cleared filesystem caches");
Ok(())
}
Ok(output) => {
let stderr = String::from_utf8_lossy(&output.stderr);
if stderr.contains("sudo: a password is required") {
warn!("Unable to clear filesystem caches: sudo password required");
warn!(
"For optimal benchmarking, configure passwordless sudo for cache clearing:"
);
warn!(" echo '$USER ALL=(ALL) NOPASSWD: /bin/sh -c echo\\\\ [0-9]\\\\ \\\\>\\\\ /proc/sys/vm/drop_caches' | sudo tee /etc/sudoers.d/drop_caches");
Ok(())
} else {
Err(eyre!("Failed to clear filesystem caches: {}", stderr))
}
}
Err(e) => {
warn!("Unable to clear filesystem caches: {}", e);
Ok(())
}
}
}
/// Run a warmup benchmark for cache warming
pub(crate) async fn run_warmup(&self, from_block: u64) -> Result<()> {
let to_block = from_block + self.warmup_blocks;
info!(
"Running warmup benchmark from block {} to {} ({} blocks)",
from_block, to_block, self.warmup_blocks
);
// Build the reth-bench command for warmup (no output flag)
let mut cmd = Command::new("reth-bench");
cmd.args([
"new-payload-fcu",
"--rpc-url",
&self.rpc_url,
"--jwt-secret",
&self.jwt_secret,
"--from",
&from_block.to_string(),
"--to",
&to_block.to_string(),
]);
// Add wait-time argument if provided
if let Some(ref wait_time) = self.wait_time {
cmd.args(["--wait-time", wait_time]);
}
cmd.env("RUST_LOG_STYLE", "never")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.kill_on_drop(true);
// Set process group for consistent signal handling
#[cfg(unix)]
{
cmd.process_group(0);
}
debug!("Executing warmup reth-bench command: {:?}", cmd);
// Execute the warmup benchmark
let mut child = cmd.spawn().wrap_err("Failed to start warmup reth-bench process")?;
// Stream output at debug level
if let Some(stdout) = child.stdout.take() {
tokio::spawn(async move {
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
debug!("[WARMUP] {}", line);
}
});
}
if let Some(stderr) = child.stderr.take() {
tokio::spawn(async move {
let reader = BufReader::new(stderr);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
debug!("[WARMUP] {}", line);
}
});
}
let status = child.wait().await.wrap_err("Failed to wait for warmup reth-bench")?;
if !status.success() {
return Err(eyre!("Warmup reth-bench failed with exit code: {:?}", status.code()));
}
info!("Warmup completed successfully");
Ok(())
}
/// Run a benchmark for the specified block range
pub(crate) async fn run_benchmark(
&self,
from_block: u64,
to_block: u64,
output_dir: &Path,
) -> Result<()> {
info!(
"Running benchmark from block {} to {} (output: {:?})",
from_block, to_block, output_dir
);
// Ensure output directory exists
std::fs::create_dir_all(output_dir)
.wrap_err_with(|| format!("Failed to create output directory: {output_dir:?}"))?;
// Create log file path for reth-bench output
let log_file_path = output_dir.join("reth_bench.log");
info!("reth-bench logs will be saved to: {:?}", log_file_path);
// Build the reth-bench command
let mut cmd = Command::new("reth-bench");
cmd.args([
"new-payload-fcu",
"--rpc-url",
&self.rpc_url,
"--jwt-secret",
&self.jwt_secret,
"--from",
&from_block.to_string(),
"--to",
&to_block.to_string(),
"--output",
&output_dir.to_string_lossy(),
]);
// Add wait-time argument if provided
if let Some(ref wait_time) = self.wait_time {
cmd.args(["--wait-time", wait_time]);
}
cmd.env("RUST_LOG_STYLE", "never")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.kill_on_drop(true);
// Set process group for consistent signal handling
#[cfg(unix)]
{
cmd.process_group(0);
}
// Debug log the command
debug!("Executing reth-bench command: {:?}", cmd);
// Execute the benchmark
let mut child = cmd.spawn().wrap_err("Failed to start reth-bench process")?;
// Capture stdout and stderr for error reporting
let stdout_lines = Arc::new(Mutex::new(Vec::new()));
let stderr_lines = Arc::new(Mutex::new(Vec::new()));
// Stream stdout with prefix at debug level, capture for error reporting, and write to log
// file
if let Some(stdout) = child.stdout.take() {
let stdout_lines_clone = stdout_lines.clone();
let log_file = AsyncFile::create(&log_file_path)
.await
.wrap_err(format!("Failed to create log file: {:?}", log_file_path))?;
tokio::spawn(async move {
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
let mut log_file = log_file;
while let Ok(Some(line)) = lines.next_line().await {
debug!("[RETH-BENCH] {}", line);
if let Ok(mut captured) = stdout_lines_clone.lock() {
captured.push(line.clone());
}
// Write to log file (reth-bench output already has timestamps if needed)
let log_line = format!("{}\n", line);
if let Err(e) = log_file.write_all(log_line.as_bytes()).await {
debug!("Failed to write to log file: {}", e);
}
}
});
}
// Stream stderr with prefix at debug level, capture for error reporting, and write to log
// file
if let Some(stderr) = child.stderr.take() {
let stderr_lines_clone = stderr_lines.clone();
let log_file = AsyncFile::options()
.create(true)
.append(true)
.open(&log_file_path)
.await
.wrap_err(format!("Failed to open log file for stderr: {:?}", log_file_path))?;
tokio::spawn(async move {
let reader = BufReader::new(stderr);
let mut lines = reader.lines();
let mut log_file = log_file;
while let Ok(Some(line)) = lines.next_line().await {
debug!("[RETH-BENCH] {}", line);
if let Ok(mut captured) = stderr_lines_clone.lock() {
captured.push(line.clone());
}
// Write to log file (reth-bench output already has timestamps if needed)
let log_line = format!("{}\n", line);
if let Err(e) = log_file.write_all(log_line.as_bytes()).await {
debug!("Failed to write to log file: {}", e);
}
}
});
}
let status = child.wait().await.wrap_err("Failed to wait for reth-bench")?;
if !status.success() {
// Print all captured output when command fails
error!("reth-bench failed with exit code: {:?}", status.code());
if let Ok(stdout) = stdout_lines.lock() &&
!stdout.is_empty()
{
error!("reth-bench stdout:");
for line in stdout.iter() {
error!(" {}", line);
}
}
if let Ok(stderr) = stderr_lines.lock() &&
!stderr.is_empty()
{
error!("reth-bench stderr:");
for line in stderr.iter() {
error!(" {}", line);
}
}
return Err(eyre!("reth-bench failed with exit code: {:?}", status.code()));
}
info!("Benchmark completed");
Ok(())
}
}

View File

@@ -0,0 +1,945 @@
//! CLI argument parsing and main command orchestration.
use alloy_provider::{Provider, ProviderBuilder};
use clap::Parser;
use eyre::{eyre, Result, WrapErr};
use reth_chainspec::Chain;
use reth_cli_runner::CliContext;
use reth_node_core::args::{DatadirArgs, LogArgs, TraceArgs};
use reth_tracing::FileWorkerGuard;
use std::{net::TcpListener, path::PathBuf, str::FromStr};
use tokio::process::Command;
use tracing::{debug, info, warn};
use crate::{
benchmark::BenchmarkRunner, comparison::ComparisonGenerator, compilation::CompilationManager,
git::GitManager, node::NodeManager,
};
/// Target for disabling the --debug.startup-sync-state-idle flag
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum DisableStartupSyncStateIdle {
/// Disable for baseline and warmup runs
Baseline,
/// Disable for feature runs only
Feature,
/// Disable for all runs
All,
}
impl FromStr for DisableStartupSyncStateIdle {
type Err = String;
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"baseline" => Ok(Self::Baseline),
"feature" => Ok(Self::Feature),
"all" => Ok(Self::All),
_ => Err(format!("Invalid value '{}'. Expected 'baseline', 'feature', or 'all'", s)),
}
}
}
impl std::fmt::Display for DisableStartupSyncStateIdle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Baseline => write!(f, "baseline"),
Self::Feature => write!(f, "feature"),
Self::All => write!(f, "all"),
}
}
}
/// Automated reth benchmark comparison between git references
#[derive(Debug, Parser)]
#[command(
name = "reth-bench-compare",
about = "Compare reth performance between two git references (branches or tags)",
version
)]
pub(crate) struct Args {
/// Git reference (branch or tag) to use as baseline for comparison
#[arg(long, value_name = "REF")]
pub baseline_ref: String,
/// Git reference (branch or tag) to compare against the baseline
#[arg(long, value_name = "REF")]
pub feature_ref: String,
#[command(flatten)]
pub datadir: DatadirArgs,
/// Number of blocks to benchmark
#[arg(long, value_name = "N", default_value = "100")]
pub blocks: u64,
/// RPC endpoint for fetching block data
#[arg(long, value_name = "URL")]
pub rpc_url: Option<String>,
/// JWT secret file path
///
/// If not provided, defaults to `<datadir>/<chain>/jwt.hex`.
/// If the file doesn't exist, it will be created automatically.
#[arg(long, value_name = "PATH")]
pub jwt_secret: Option<PathBuf>,
/// Output directory for benchmark results
#[arg(long, value_name = "PATH", default_value = "./reth-bench-compare")]
pub output_dir: String,
/// Skip git branch validation (useful for testing)
#[arg(long)]
pub skip_git_validation: bool,
/// Port for reth metrics endpoint
#[arg(long, value_name = "PORT", default_value = "5005")]
pub metrics_port: u16,
/// The chain this node is running.
///
/// Possible values are either a built-in chain name or numeric chain ID.
#[arg(long, value_name = "CHAIN", default_value = "mainnet", required = false)]
pub chain: Chain,
/// Run reth binary with sudo (for elevated privileges)
#[arg(long)]
pub sudo: bool,
/// Generate comparison charts using Python script
#[arg(long)]
pub draw: bool,
/// Enable CPU profiling with samply during benchmark runs
#[arg(long)]
pub profile: bool,
/// Wait time between engine API calls (passed to reth-bench)
#[arg(long, value_name = "DURATION")]
pub wait_time: Option<String>,
/// Number of blocks to run for cache warmup after clearing caches.
/// If not specified, defaults to the same as --blocks
#[arg(long, value_name = "N")]
pub warmup_blocks: Option<u64>,
/// Disable filesystem cache clearing before warmup phase.
/// By default, filesystem caches are cleared before warmup to ensure consistent benchmarks.
#[arg(long)]
pub no_clear_cache: bool,
#[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..."`
#[arg(long, value_name = "ARGS")]
pub baseline_args: Option<String>,
/// Additional arguments to pass to feature reth node command
///
/// Example: `--feature-args "--debug.tip 0xdef..."`
#[arg(long, value_name = "ARGS")]
pub feature_args: Option<String>,
/// Additional arguments to pass to reth node command (applied to both baseline and feature)
///
/// All arguments after `--` will be passed directly to the reth node command.
/// Example: `reth-bench-compare --baseline-ref main --feature-ref pr/123 -- --debug.tip
/// 0xabc...`
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
pub reth_args: Vec<String>,
/// Comma-separated list of features to enable during reth compilation
///
/// Example: `jemalloc,asm-keccak`
#[arg(long, value_name = "FEATURES", default_value = "jemalloc,asm-keccak")]
pub features: String,
/// Disable automatic --debug.startup-sync-state-idle flag for specific runs.
/// Can be "baseline", "feature", or "all".
/// By default, the flag is passed to warmup, baseline, and feature runs.
/// When "baseline" is specified, the flag is NOT passed to warmup OR baseline.
/// When "feature" is specified, the flag is NOT passed to feature.
/// When "all" is specified, the flag is NOT passed to any run.
#[arg(long, value_name = "TARGET")]
pub disable_startup_sync_state_idle: Option<DisableStartupSyncStateIdle>,
}
impl Args {
/// Initializes tracing with the configured options.
pub(crate) fn init_tracing(&self) -> Result<Option<FileWorkerGuard>> {
let guard = self.logs.init_tracing()?;
Ok(guard)
}
/// Build additional arguments for a specific ref type, conditionally including
/// --debug.startup-sync-state-idle based on the configuration
pub(crate) fn build_additional_args(
&self,
ref_type: &str,
base_args_str: Option<&String>,
) -> Vec<String> {
// Parse the base arguments string if provided
let mut args = base_args_str.map(|s| parse_args_string(s)).unwrap_or_default();
// Determine if we should add the --debug.startup-sync-state-idle flag
let should_add_flag = match self.disable_startup_sync_state_idle {
None => true, // By default, add the flag
Some(DisableStartupSyncStateIdle::All) => false,
Some(DisableStartupSyncStateIdle::Baseline) => {
ref_type != "baseline" && ref_type != "warmup"
}
Some(DisableStartupSyncStateIdle::Feature) => ref_type != "feature",
};
if should_add_flag {
args.push("--debug.startup-sync-state-idle".to_string());
debug!("Adding --debug.startup-sync-state-idle flag for ref_type: {}", ref_type);
} else {
debug!("Skipping --debug.startup-sync-state-idle flag for ref_type: {}", ref_type);
}
args
}
/// Get the default RPC URL for a given chain
const fn get_default_rpc_url(chain: &Chain) -> &'static str {
match chain.id() {
8453 => "https://base-mainnet.rpc.ithaca.xyz", // base
84532 => "https://base-sepolia.rpc.ithaca.xyz", // base-sepolia
27082 => "https://rpc.hoodi.ethpandaops.io", // hoodi
_ => "https://reth-ethereum.ithaca.xyz/rpc", // mainnet and fallback
}
}
/// Get the RPC URL, using chain-specific default if not provided
pub(crate) fn get_rpc_url(&self) -> String {
self.rpc_url.clone().unwrap_or_else(|| Self::get_default_rpc_url(&self.chain).to_string())
}
/// Get the JWT secret path - either provided or derived from datadir
pub(crate) fn jwt_secret_path(&self) -> PathBuf {
match &self.jwt_secret {
Some(path) => {
let jwt_secret_str = path.to_string_lossy();
let expanded = shellexpand::tilde(&jwt_secret_str);
PathBuf::from(expanded.as_ref())
}
None => {
// Use the same logic as reth: <datadir>/<chain>/jwt.hex
let chain_path = self.datadir.clone().resolve_datadir(self.chain);
chain_path.jwt()
}
}
}
/// Get the resolved datadir path using the chain
pub(crate) fn datadir_path(&self) -> PathBuf {
let chain_path = self.datadir.clone().resolve_datadir(self.chain);
chain_path.data_dir().to_path_buf()
}
/// Get the expanded output directory path
pub(crate) fn output_dir_path(&self) -> PathBuf {
let expanded = shellexpand::tilde(&self.output_dir);
PathBuf::from(expanded.as_ref())
}
/// Get the effective warmup blocks value - either specified or defaults to blocks
pub(crate) fn get_warmup_blocks(&self) -> u64 {
self.warmup_blocks.unwrap_or(self.blocks)
}
}
/// Validate that the RPC endpoint chain ID matches the specified chain
async fn validate_rpc_chain_id(rpc_url: &str, expected_chain: &Chain) -> Result<()> {
// Create Alloy provider
let url = rpc_url.parse().map_err(|e| eyre!("Invalid RPC URL '{}': {}", rpc_url, e))?;
let provider = ProviderBuilder::new().connect_http(url);
// Query chain ID using Alloy
let rpc_chain_id = provider
.get_chain_id()
.await
.map_err(|e| eyre!("Failed to get chain ID from RPC endpoint {}: {:?}", rpc_url, e))?;
let expected_chain_id = expected_chain.id();
if rpc_chain_id != expected_chain_id {
return Err(eyre!(
"RPC endpoint chain ID mismatch!\n\
Expected: {} (chain: {})\n\
Found: {} at RPC endpoint: {}\n\n\
Please use an RPC endpoint for the correct network or change the --chain argument.",
expected_chain_id,
expected_chain,
rpc_chain_id,
rpc_url
));
}
info!("Validated RPC endpoint chain ID");
Ok(())
}
/// Main comparison workflow execution
pub(crate) async fn run_comparison(args: Args, _ctx: CliContext) -> Result<()> {
// Create a new process group for this process and all its children
#[cfg(unix)]
{
use nix::unistd::{getpid, setpgid};
if let Err(e) = setpgid(getpid(), getpid()) {
warn!("Failed to create process group: {e}");
}
}
info!(
"Starting benchmark comparison between '{}' and '{}'",
args.baseline_ref, args.feature_ref
);
if args.sudo {
info!("Running in sudo mode - reth commands will use elevated privileges");
}
// Initialize Git manager
let git_manager = GitManager::new()?;
// Fetch all branches, tags, and commits
git_manager.fetch_all()?;
// Initialize compilation manager
let output_dir = args.output_dir_path();
let compilation_manager = CompilationManager::new(
git_manager.repo_root().to_string(),
output_dir.clone(),
git_manager.clone(),
args.features.clone(),
)?;
// Initialize node manager
let mut node_manager = NodeManager::new(&args);
let benchmark_runner = BenchmarkRunner::new(&args);
let mut comparison_generator = ComparisonGenerator::new(&args);
// Set the comparison directory in node manager to align with results directory
node_manager.set_comparison_dir(comparison_generator.get_output_dir());
// Store original git state for restoration
let original_ref = git_manager.get_current_ref()?;
info!("Current git reference: {}", original_ref);
// Validate git state
if !args.skip_git_validation {
git_manager.validate_clean_state()?;
git_manager.validate_refs(&[&args.baseline_ref, &args.feature_ref])?;
}
// Validate RPC endpoint chain ID matches the specified chain
let rpc_url = args.get_rpc_url();
validate_rpc_chain_id(&rpc_url, &args.chain).await?;
// Setup signal handling for cleanup
let git_manager_cleanup = git_manager.clone();
let original_ref_cleanup = original_ref.clone();
ctrlc::set_handler(move || {
eprintln!("Received interrupt signal, cleaning up...");
// Send SIGTERM to entire process group to ensure all children exit
#[cfg(unix)]
{
use nix::{
sys::signal::{kill, Signal},
unistd::Pid,
};
// Send SIGTERM to our process group (negative PID = process group)
let current_pid = std::process::id() as i32;
let pgid = Pid::from_raw(-current_pid);
if let Err(e) = kill(pgid, Signal::SIGTERM) {
eprintln!("Failed to send SIGTERM to process group: {e}");
}
}
// Give a moment for any ongoing git operations to complete
std::thread::sleep(std::time::Duration::from_millis(200));
if let Err(e) = git_manager_cleanup.switch_ref(&original_ref_cleanup) {
eprintln!("Failed to restore original git reference: {e}");
eprintln!("You may need to manually run: git checkout {original_ref_cleanup}");
}
std::process::exit(1);
})?;
let result = run_benchmark_workflow(
&git_manager,
&compilation_manager,
&mut node_manager,
&benchmark_runner,
&mut comparison_generator,
&args,
)
.await;
// Always restore original git reference
info!("Restoring original git reference: {}", original_ref);
git_manager.switch_ref(&original_ref)?;
// Handle any errors from the workflow
result?;
Ok(())
}
/// Parse a string of arguments into a vector of strings
fn parse_args_string(args_str: &str) -> Vec<String> {
shlex::split(args_str).unwrap_or_else(|| {
// Fallback to simple whitespace splitting if shlex fails
args_str.split_whitespace().map(|s| s.to_string()).collect()
})
}
/// Run compilation phase for both baseline and feature binaries
async fn run_compilation_phase(
git_manager: &GitManager,
compilation_manager: &CompilationManager,
args: &Args,
is_optimism: bool,
) -> Result<(String, String)> {
info!("=== Running compilation phase ===");
// Ensure required tools are available (only need to check once)
compilation_manager.ensure_reth_bench_available()?;
if args.profile {
compilation_manager.ensure_samply_available()?;
}
let refs = [&args.baseline_ref, &args.feature_ref];
let ref_types = ["baseline", "feature"];
// First, resolve all refs to commits using a HashMap to avoid race conditions where a ref is
// pushed to mid-run.
let mut ref_commits = std::collections::HashMap::new();
for &git_ref in &refs {
if !ref_commits.contains_key(git_ref) {
git_manager.switch_ref(git_ref)?;
let commit = git_manager.get_current_commit()?;
ref_commits.insert(git_ref.clone(), commit);
info!("Reference {} resolves to commit: {}", git_ref, &ref_commits[git_ref][..8]);
}
}
// Now compile each ref using the resolved commits
for (i, &git_ref) in refs.iter().enumerate() {
let ref_type = ref_types[i];
let commit = &ref_commits[git_ref];
info!(
"Compiling {} binary for reference: {} (commit: {})",
ref_type,
git_ref,
&commit[..8]
);
// Switch to target reference
git_manager.switch_ref(git_ref)?;
// Compile reth (with caching)
compilation_manager.compile_reth(commit, is_optimism)?;
info!("Completed compilation for {} reference", ref_type);
}
let baseline_commit = ref_commits[&args.baseline_ref].clone();
let feature_commit = ref_commits[&args.feature_ref].clone();
info!("Compilation phase completed");
Ok((baseline_commit, feature_commit))
}
/// Run warmup phase to warm up caches before benchmarking
async fn run_warmup_phase(
git_manager: &GitManager,
compilation_manager: &CompilationManager,
node_manager: &mut NodeManager,
benchmark_runner: &BenchmarkRunner,
args: &Args,
is_optimism: bool,
baseline_commit: &str,
) -> Result<()> {
info!("=== Running warmup phase ===");
// Use baseline for warmup
let warmup_ref = &args.baseline_ref;
// Switch to baseline reference
git_manager.switch_ref(warmup_ref)?;
// Get the cached binary path for baseline (should already be compiled)
let binary_path =
compilation_manager.get_cached_binary_path_for_commit(baseline_commit, is_optimism);
// Verify the cached binary exists
if !binary_path.exists() {
return Err(eyre!(
"Cached baseline binary not found at {:?}. Compilation phase should have created it.",
binary_path
));
}
info!("Using cached baseline binary for warmup (commit: {})", &baseline_commit[..8]);
// 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 (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
let current_tip = node_manager.wait_for_node_ready_and_get_tip().await?;
info!("Warmup node is ready at tip: {}", current_tip);
// Store the tip we'll unwind back to
let original_tip = current_tip;
// Clear filesystem caches before warmup run only (unless disabled)
if args.no_clear_cache {
info!("Skipping filesystem cache clearing (--no-clear-cache flag set)");
} else {
BenchmarkRunner::clear_fs_caches().await?;
}
// Run warmup to warm up caches
benchmark_runner.run_warmup(current_tip).await?;
// Stop node before unwinding (node must be stopped to release database lock)
node_manager.stop_node(&mut node_process).await?;
// Unwind back to starting block after warmup
node_manager.unwind_to_block(original_tip).await?;
info!("Warmup phase completed");
Ok(())
}
/// Execute the complete benchmark workflow for both branches
async fn run_benchmark_workflow(
git_manager: &GitManager,
compilation_manager: &CompilationManager,
node_manager: &mut NodeManager,
benchmark_runner: &BenchmarkRunner,
comparison_generator: &mut ComparisonGenerator,
args: &Args,
) -> Result<()> {
// Detect if this is an Optimism chain once at the beginning
let rpc_url = args.get_rpc_url();
let is_optimism = compilation_manager.detect_optimism_chain(&rpc_url).await?;
// Run compilation phase for both binaries
let (baseline_commit, feature_commit) =
run_compilation_phase(git_manager, compilation_manager, args, is_optimism).await?;
// Run warmup phase before benchmarking (skip if warmup_blocks is 0)
if args.get_warmup_blocks() > 0 {
run_warmup_phase(
git_manager,
compilation_manager,
node_manager,
benchmark_runner,
args,
is_optimism,
&baseline_commit,
)
.await?;
} else {
info!("Skipping warmup phase (warmup_blocks is 0)");
}
let refs = [&args.baseline_ref, &args.feature_ref];
let ref_types = ["baseline", "feature"];
let commits = [&baseline_commit, &feature_commit];
for (i, &git_ref) in refs.iter().enumerate() {
let ref_type = ref_types[i];
let commit = commits[i];
info!("=== Processing {} reference: {} ===", ref_type, git_ref);
// Switch to target reference
git_manager.switch_ref(git_ref)?;
// Get the cached binary path for this git reference (should already be compiled)
let binary_path =
compilation_manager.get_cached_binary_path_for_commit(commit, is_optimism);
// Verify the cached binary exists
if !binary_path.exists() {
return Err(eyre!(
"Cached {} binary not found at {:?}. Compilation phase should have created it.",
ref_type,
binary_path
));
}
info!("Using cached {} binary (commit: {})", ref_type, &commit[..8]);
// Get reference-specific base arguments string
let base_args_str = match ref_type {
"baseline" => args.baseline_args.as_ref(),
"feature" => args.feature_args.as_ref(),
_ => None,
};
// 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 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)
let current_tip = node_manager.wait_for_node_ready_and_get_tip().await?;
info!("Node is ready at tip: {}", current_tip);
// Store the tip we'll unwind back to
let original_tip = current_tip;
// Calculate benchmark range
// Note: reth-bench has an off-by-one error where it consumes the first block
// of the range, so we add 1 to compensate and get exactly args.blocks blocks
let from_block = original_tip;
let to_block = original_tip + args.blocks;
// Run benchmark
let output_dir = comparison_generator.get_ref_output_dir(ref_type);
// Capture start timestamp for the benchmark run
let benchmark_start = chrono::Utc::now();
// Run benchmark (comparison logic is handled separately by ComparisonGenerator)
benchmark_runner.run_benchmark(from_block, to_block, &output_dir).await?;
// Capture end timestamp for the benchmark run
let benchmark_end = chrono::Utc::now();
// Stop node
node_manager.stop_node(&mut node_process).await?;
// Unwind back to original tip
node_manager.unwind_to_block(original_tip).await?;
// Store results for comparison
comparison_generator.add_ref_results(ref_type, &output_dir)?;
// 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);
}
// Generate comparison report
comparison_generator.generate_comparison_report().await?;
// Generate charts if requested
if args.draw {
generate_comparison_charts(comparison_generator).await?;
}
// Start samply servers if profiling was enabled
if args.profile {
start_samply_servers(args).await?;
}
Ok(())
}
/// Generate comparison charts using the Python script
async fn generate_comparison_charts(comparison_generator: &ComparisonGenerator) -> Result<()> {
info!("Generating comparison charts with Python script...");
let baseline_output_dir = comparison_generator.get_ref_output_dir("baseline");
let feature_output_dir = comparison_generator.get_ref_output_dir("feature");
let baseline_csv = baseline_output_dir.join("combined_latency.csv");
let feature_csv = feature_output_dir.join("combined_latency.csv");
// Check if CSV files exist
if !baseline_csv.exists() {
return Err(eyre!("Baseline CSV not found: {:?}", baseline_csv));
}
if !feature_csv.exists() {
return Err(eyre!("Feature CSV not found: {:?}", feature_csv));
}
let output_dir = comparison_generator.get_output_dir();
let chart_output = output_dir.join("latency_comparison.png");
let script_path = "bin/reth-bench/scripts/compare_newpayload_latency.py";
info!("Running Python comparison script with uv...");
let mut cmd = Command::new("uv");
cmd.args([
"run",
script_path,
&baseline_csv.to_string_lossy(),
&feature_csv.to_string_lossy(),
"-o",
&chart_output.to_string_lossy(),
]);
// Set process group for consistent signal handling
#[cfg(unix)]
{
cmd.process_group(0);
}
let output = cmd.output().await.map_err(|e| {
eyre!("Failed to execute Python script with uv: {}. Make sure uv is installed.", e)
})?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
return Err(eyre!(
"Python script failed with exit code {:?}:\nstdout: {}\nstderr: {}",
output.status.code(),
stdout,
stderr
));
}
let stdout = String::from_utf8_lossy(&output.stdout);
if !stdout.trim().is_empty() {
info!("Python script output:\n{}", stdout);
}
info!("Comparison chart generated: {:?}", chart_output);
Ok(())
}
/// Start samply servers for viewing profiles
async fn start_samply_servers(args: &Args) -> Result<()> {
info!("Starting samply servers for profile viewing...");
let output_dir = args.output_dir_path();
let profiles_dir = output_dir.join("profiles");
// Build profile paths
let baseline_profile = profiles_dir.join("baseline.json.gz");
let feature_profile = profiles_dir.join("feature.json.gz");
// Check if profiles exist
if !baseline_profile.exists() {
warn!("Baseline profile not found: {:?}", baseline_profile);
return Ok(());
}
if !feature_profile.exists() {
warn!("Feature profile not found: {:?}", feature_profile);
return Ok(());
}
// Find two consecutive available ports starting from 3000
let (baseline_port, feature_port) = find_consecutive_ports(3000)?;
info!("Found available ports: {} and {}", baseline_port, feature_port);
// Get samply path
let samply_path = get_samply_path().await?;
// Start baseline server
info!("Starting samply server for baseline '{}' on port {}", args.baseline_ref, baseline_port);
let mut baseline_cmd = Command::new(&samply_path);
baseline_cmd
.args(["load", "--port", &baseline_port.to_string(), &baseline_profile.to_string_lossy()])
.kill_on_drop(true);
// Set process group for consistent signal handling
#[cfg(unix)]
{
baseline_cmd.process_group(0);
}
// Conditionally pipe output based on log level
if tracing::enabled!(tracing::Level::DEBUG) {
baseline_cmd.stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::piped());
} else {
baseline_cmd.stdout(std::process::Stdio::null()).stderr(std::process::Stdio::null());
}
// Debug log the command
debug!("Executing samply load command: {:?}", baseline_cmd);
let mut baseline_child =
baseline_cmd.spawn().wrap_err("Failed to start samply server for baseline")?;
// Stream baseline samply output if debug logging is enabled
if tracing::enabled!(tracing::Level::DEBUG) {
if let Some(stdout) = baseline_child.stdout.take() {
tokio::spawn(async move {
use tokio::io::{AsyncBufReadExt, BufReader};
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
debug!("[SAMPLY-BASELINE] {}", line);
}
});
}
if let Some(stderr) = baseline_child.stderr.take() {
tokio::spawn(async move {
use tokio::io::{AsyncBufReadExt, BufReader};
let reader = BufReader::new(stderr);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
debug!("[SAMPLY-BASELINE] {}", line);
}
});
}
}
// Start feature server
info!("Starting samply server for feature '{}' on port {}", args.feature_ref, feature_port);
let mut feature_cmd = Command::new(&samply_path);
feature_cmd
.args(["load", "--port", &feature_port.to_string(), &feature_profile.to_string_lossy()])
.kill_on_drop(true);
// Set process group for consistent signal handling
#[cfg(unix)]
{
feature_cmd.process_group(0);
}
// Conditionally pipe output based on log level
if tracing::enabled!(tracing::Level::DEBUG) {
feature_cmd.stdout(std::process::Stdio::piped()).stderr(std::process::Stdio::piped());
} else {
feature_cmd.stdout(std::process::Stdio::null()).stderr(std::process::Stdio::null());
}
// Debug log the command
debug!("Executing samply load command: {:?}", feature_cmd);
let mut feature_child =
feature_cmd.spawn().wrap_err("Failed to start samply server for feature")?;
// Stream feature samply output if debug logging is enabled
if tracing::enabled!(tracing::Level::DEBUG) {
if let Some(stdout) = feature_child.stdout.take() {
tokio::spawn(async move {
use tokio::io::{AsyncBufReadExt, BufReader};
let reader = BufReader::new(stdout);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
debug!("[SAMPLY-FEATURE] {}", line);
}
});
}
if let Some(stderr) = feature_child.stderr.take() {
tokio::spawn(async move {
use tokio::io::{AsyncBufReadExt, BufReader};
let reader = BufReader::new(stderr);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
debug!("[SAMPLY-FEATURE] {}", line);
}
});
}
}
// Give servers time to start
tokio::time::sleep(std::time::Duration::from_secs(2)).await;
// Print access information
println!("\n=== SAMPLY PROFILE SERVERS STARTED ===");
println!("Baseline '{}': http://127.0.0.1:{}", args.baseline_ref, baseline_port);
println!("Feature '{}': http://127.0.0.1:{}", args.feature_ref, feature_port);
println!("\nOpen the URLs in your browser to view the profiles.");
println!("Press Ctrl+C to stop the servers and exit.");
println!("=========================================\n");
// Wait for Ctrl+C or process termination
let ctrl_c = tokio::signal::ctrl_c();
let baseline_wait = baseline_child.wait();
let feature_wait = feature_child.wait();
tokio::select! {
_ = ctrl_c => {
info!("Received Ctrl+C, shutting down samply servers...");
}
result = baseline_wait => {
match result {
Ok(status) => info!("Baseline samply server exited with status: {}", status),
Err(e) => warn!("Baseline samply server error: {}", e),
}
}
result = feature_wait => {
match result {
Ok(status) => info!("Feature samply server exited with status: {}", status),
Err(e) => warn!("Feature samply server error: {}", e),
}
}
}
// Ensure both processes are terminated
let _ = baseline_child.kill().await;
let _ = feature_child.kill().await;
info!("Samply servers stopped.");
Ok(())
}
/// Find two consecutive available ports starting from the given port
fn find_consecutive_ports(start_port: u16) -> Result<(u16, u16)> {
for port in start_port..=65533 {
// Check if both port and port+1 are available
if is_port_available(port) && is_port_available(port + 1) {
return Ok((port, port + 1));
}
}
Err(eyre!("Could not find two consecutive available ports starting from {}", start_port))
}
/// Check if a port is available by attempting to bind to it
fn is_port_available(port: u16) -> bool {
TcpListener::bind(("127.0.0.1", port)).is_ok()
}
/// Get the absolute path to samply using 'which' command
async fn get_samply_path() -> Result<String> {
let output = Command::new("which")
.arg("samply")
.output()
.await
.wrap_err("Failed to execute 'which samply' command")?;
if !output.status.success() {
return Err(eyre!("samply not found in PATH"));
}
let samply_path = String::from_utf8(output.stdout)
.wrap_err("samply path is not valid UTF-8")?
.trim()
.to_string();
if samply_path.is_empty() {
return Err(eyre!("which samply returned empty path"));
}
Ok(samply_path)
}

View File

@@ -0,0 +1,710 @@
//! Results comparison and report generation.
use crate::cli::Args;
use chrono::{DateTime, Utc};
use csv::Reader;
use eyre::{eyre, Result, WrapErr};
use serde::{Deserialize, Serialize};
use std::{
cmp::Ordering,
collections::HashMap,
fs,
path::{Path, PathBuf},
};
use tracing::{info, warn};
/// Manages comparison between baseline and feature reference results
pub(crate) struct ComparisonGenerator {
output_dir: PathBuf,
timestamp: String,
baseline_ref_name: String,
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
#[derive(Debug, Clone)]
pub(crate) struct BenchmarkResults {
pub ref_name: String,
pub combined_latency_data: Vec<CombinedLatencyRow>,
pub summary: BenchmarkSummary,
pub start_timestamp: Option<DateTime<Utc>>,
pub end_timestamp: Option<DateTime<Utc>>,
}
/// Combined latency CSV row structure
#[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,
}
/// Total gas CSV row structure
#[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.
///
/// 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 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
#[derive(Debug, Serialize)]
pub(crate) struct ComparisonReport {
pub timestamp: String,
pub baseline: RefInfo,
pub feature: RefInfo,
pub comparison_summary: ComparisonSummary,
pub per_block_comparisons: Vec<BlockComparison>,
}
/// Information about a reference in the comparison
#[derive(Debug, Serialize)]
pub(crate) struct RefInfo {
pub ref_name: String,
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.
///
/// 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 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,
}
/// Per-block comparison data
#[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,
}
impl ComparisonGenerator {
/// Create a new comparison generator
pub(crate) fn new(args: &Args) -> Self {
let now: DateTime<Utc> = Utc::now();
let timestamp = now.format("%Y%m%d_%H%M%S").to_string();
Self {
output_dir: args.output_dir_path(),
timestamp,
baseline_ref_name: args.baseline_ref.clone(),
feature_ref_name: args.feature_ref.clone(),
baseline_results: None,
feature_results: None,
baseline_command: None,
feature_command: None,
}
}
/// Get the output directory for a specific reference
pub(crate) fn get_ref_output_dir(&self, ref_type: &str) -> PathBuf {
self.output_dir.join("results").join(&self.timestamp).join(ref_type)
}
/// Get the main output directory for this comparison run
pub(crate) fn get_output_dir(&self) -> PathBuf {
self.output_dir.join("results").join(&self.timestamp)
}
/// Add benchmark results for a reference
pub(crate) fn add_ref_results(&mut self, ref_type: &str, output_path: &Path) -> Result<()> {
let ref_name = match ref_type {
"baseline" => &self.baseline_ref_name,
"feature" => &self.feature_ref_name,
_ => return Err(eyre!("Unknown reference type: {}", ref_type)),
};
let results = self.load_benchmark_results(ref_name, output_path)?;
match ref_type {
"baseline" => self.baseline_results = Some(results),
"feature" => self.feature_results = Some(results),
_ => return Err(eyre!("Unknown reference type: {}", ref_type)),
}
info!("Loaded benchmark results for {} reference", ref_type);
Ok(())
}
/// Set the benchmark run timestamps for a reference
pub(crate) fn set_ref_timestamps(
&mut self,
ref_type: &str,
start: DateTime<Utc>,
end: DateTime<Utc>,
) -> Result<()> {
match ref_type {
"baseline" => {
if let Some(ref mut results) = self.baseline_results {
results.start_timestamp = Some(start);
results.end_timestamp = Some(end);
} else {
return Err(eyre!("Baseline results not loaded yet"));
}
}
"feature" => {
if let Some(ref mut results) = self.feature_results {
results.start_timestamp = Some(start);
results.end_timestamp = Some(end);
} else {
return Err(eyre!("Feature results not loaded yet"));
}
}
_ => return Err(eyre!("Unknown reference type: {}", ref_type)),
}
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...");
let baseline =
self.baseline_results.as_ref().ok_or_else(|| eyre!("Baseline results not loaded"))?;
let feature =
self.feature_results.as_ref().ok_or_else(|| eyre!("Feature results not loaded"))?;
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(),
baseline: RefInfo {
ref_name: baseline.ref_name.clone(),
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,
};
// Write reports
self.write_comparison_reports(&report).await?;
// Print summary to console
self.print_comparison_summary(&report);
Ok(())
}
/// Load benchmark results from CSV files
fn load_benchmark_results(
&self,
ref_name: &str,
output_path: &Path,
) -> Result<BenchmarkResults> {
let combined_latency_path = output_path.join("combined_latency.csv");
let total_gas_path = output_path.join("total_gas.csv");
let combined_latency_data = self.load_combined_latency_csv(&combined_latency_path)?;
let total_gas_data = self.load_total_gas_csv(&total_gas_path)?;
let summary = self.calculate_summary(&combined_latency_data, &total_gas_data)?;
Ok(BenchmarkResults {
ref_name: ref_name.to_string(),
combined_latency_data,
summary,
start_timestamp: None,
end_timestamp: None,
})
}
/// Load combined latency CSV data
fn load_combined_latency_csv(&self, path: &Path) -> Result<Vec<CombinedLatencyRow>> {
let mut reader = Reader::from_path(path)
.wrap_err_with(|| format!("Failed to open combined latency CSV: {path:?}"))?;
let mut rows = Vec::new();
for result in reader.deserialize() {
let row: CombinedLatencyRow = result
.wrap_err_with(|| format!("Failed to parse combined latency row in {path:?}"))?;
rows.push(row);
}
if rows.is_empty() {
return Err(eyre!("No data found in combined latency CSV: {:?}", path));
}
Ok(rows)
}
/// Load total gas CSV data
fn load_total_gas_csv(&self, path: &Path) -> Result<Vec<TotalGasRow>> {
let mut reader = Reader::from_path(path)
.wrap_err_with(|| format!("Failed to open total gas CSV: {path:?}"))?;
let mut rows = Vec::new();
for result in reader.deserialize() {
let row: TotalGasRow =
result.wrap_err_with(|| format!("Failed to parse total gas row in {path:?}"))?;
rows.push(row);
}
if rows.is_empty() {
return Err(eyre!("No data found in total gas CSV: {:?}", path));
}
Ok(rows)
}
/// 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],
total_gas_data: &[TotalGasRow],
) -> Result<BenchmarkSummary> {
if combined_data.is_empty() || total_gas_data.is_empty() {
return Err(eyre!("Cannot calculate summary for empty data"));
}
let total_blocks = combined_data.len() as u64;
let total_gas_used: u64 = combined_data.iter().map(|r| r.gas_used).sum();
let total_duration_ms = total_gas_data.last().unwrap().time / 1000; // Convert microseconds to milliseconds
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 {
total_gas_used as f64 / total_duration_seconds
} else {
0.0
};
let blocks_per_second = if total_duration_seconds > f64::EPSILON {
total_blocks as f64 / total_duration_seconds
} else {
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,
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,
})
}
/// Calculate comparison summary between baseline and feature
fn calculate_comparison_summary(
&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 {
((feature - baseline) / baseline) * 100.0
} else {
0.0
}
};
// 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 {
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,
feature.gas_per_second,
),
blocks_per_second_change_percent: calc_percent_change(
baseline.blocks_per_second,
feature.blocks_per_second,
),
})
}
/// Calculate per-block comparisons
fn calculate_per_block_comparisons(
&self,
baseline: &BenchmarkResults,
feature: &BenchmarkResults,
) -> Result<Vec<BlockComparison>> {
let mut baseline_map: HashMap<u64, &CombinedLatencyRow> = HashMap::new();
for row in &baseline.combined_latency_data {
baseline_map.insert(row.block_number, row);
}
let mut comparisons = Vec::new();
for feature_row in &feature.combined_latency_data {
if let Some(baseline_row) = baseline_map.get(&feature_row.block_number) {
let calc_percent_change = |baseline: u128, feature: u128| -> f64 {
if baseline > 0 {
((feature as f64 - baseline as f64) / baseline as f64) * 100.0
} else {
0.0
}
};
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(
baseline_row.new_payload_latency,
feature_row.new_payload_latency,
),
};
comparisons.push(comparison);
} else {
warn!("Block {} not found in baseline data", feature_row.block_number);
}
}
Ok(comparisons)
}
/// Write comparison reports to files
async fn write_comparison_reports(&self, report: &ComparisonReport) -> Result<()> {
let report_dir = self.output_dir.join("results").join(&self.timestamp);
fs::create_dir_all(&report_dir)
.wrap_err_with(|| format!("Failed to create report directory: {report_dir:?}"))?;
// Write JSON report
let json_path = report_dir.join("comparison_report.json");
let json_content = serde_json::to_string_pretty(report)
.wrap_err("Failed to serialize comparison report to JSON")?;
fs::write(&json_path, json_content)
.wrap_err_with(|| format!("Failed to write JSON report: {json_path:?}"))?;
// Write CSV report for per-block comparisons
let csv_path = report_dir.join("per_block_comparison.csv");
let mut writer = csv::Writer::from_path(&csv_path)
.wrap_err_with(|| format!("Failed to create CSV writer: {csv_path:?}"))?;
for comparison in &report.per_block_comparisons {
writer.serialize(comparison).wrap_err("Failed to write comparison row to CSV")?;
}
writer.flush().wrap_err("Failed to flush CSV writer")?;
info!("Comparison reports written to: {:?}", report_dir);
Ok(())
}
/// Print comparison summary to console
fn print_comparison_summary(&self, report: &ComparisonReport) {
// Parse and format timestamp nicely
let formatted_timestamp = if let Ok(dt) = chrono::DateTime::parse_from_str(
&format!("{} +0000", report.timestamp.replace('_', " ")),
"%Y%m%d %H%M%S %z",
) {
dt.format("%Y-%m-%d %H:%M:%S UTC").to_string()
} else {
// Fallback to original if parsing fails
report.timestamp.clone()
};
println!("\n=== BENCHMARK COMPARISON SUMMARY ===");
println!("Timestamp: {formatted_timestamp}");
println!("Baseline: {}", report.baseline.ref_name);
println!("Feature: {}", report.feature.ref_name);
println!();
let summary = &report.comparison_summary;
println!("Performance Changes:");
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: {} (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!(" 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)
{
println!(
" Started: {}, Ended: {}",
start.format("%Y-%m-%d %H:%M:%S UTC"),
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: {} (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!(" 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)
{
println!(
" Started: {}, Ended: {}",
start.format("%Y-%m-%d %H:%M:%S UTC"),
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

@@ -0,0 +1,354 @@
//! Compilation operations for reth and reth-bench.
use crate::git::GitManager;
use alloy_primitives::address;
use alloy_provider::{Provider, ProviderBuilder};
use eyre::{eyre, Result, WrapErr};
use std::{fs, path::PathBuf, process::Command};
use tracing::{debug, error, info, warn};
/// Manages compilation operations for reth components
#[derive(Debug)]
pub(crate) struct CompilationManager {
repo_root: String,
output_dir: PathBuf,
git_manager: GitManager,
features: String,
}
impl CompilationManager {
/// Create a new `CompilationManager`
pub(crate) const fn new(
repo_root: String,
output_dir: PathBuf,
git_manager: GitManager,
features: String,
) -> Result<Self> {
Ok(Self { repo_root, output_dir, git_manager, features })
}
/// Detect if the RPC endpoint is an Optimism chain
pub(crate) async fn detect_optimism_chain(&self, rpc_url: &str) -> Result<bool> {
info!("Detecting chain type from RPC endpoint...");
// Create Alloy provider
let url = rpc_url.parse().map_err(|e| eyre!("Invalid RPC URL '{}': {}", rpc_url, e))?;
let provider = ProviderBuilder::new().connect_http(url);
// Check for Optimism predeploy at address 0x420000000000000000000000000000000000000F
let is_optimism = !provider
.get_code_at(address!("0x420000000000000000000000000000000000000F"))
.await?
.is_empty();
if is_optimism {
info!("Detected Optimism chain");
} else {
info!("Detected Ethereum chain");
}
Ok(is_optimism)
}
/// Get the path to the cached binary using explicit commit hash
pub(crate) fn get_cached_binary_path_for_commit(
&self,
commit: &str,
is_optimism: bool,
) -> PathBuf {
let identifier = &commit[..8]; // Use first 8 chars of commit
let binary_name = if is_optimism {
format!("op-reth_{}", identifier)
} else {
format!("reth_{}", identifier)
};
self.output_dir.join("bin").join(binary_name)
}
/// Compile reth using cargo build and cache the binary
pub(crate) fn compile_reth(&self, commit: &str, is_optimism: bool) -> Result<()> {
// Validate that current git commit matches the expected commit
let current_commit = self.git_manager.get_current_commit()?;
if current_commit != commit {
return Err(eyre!(
"Git commit mismatch! Expected: {}, but currently at: {}",
&commit[..8],
&current_commit[..8]
));
}
let cached_path = self.get_cached_binary_path_for_commit(commit, is_optimism);
// Check if cached binary already exists (since path contains commit hash, it's valid)
if cached_path.exists() {
info!("Using cached binary (commit: {})", &commit[..8]);
return Ok(());
}
info!("No cached binary found, compiling (commit: {})...", &commit[..8]);
let binary_name = if is_optimism { "op-reth" } else { "reth" };
info!(
"Compiling {} with profiling configuration (commit: {})...",
binary_name,
&commit[..8]
);
let mut cmd = Command::new("cargo");
cmd.arg("build").arg("--profile").arg("profiling");
// Add features
cmd.arg("--features").arg(&self.features);
info!("Using features: {}", self.features);
// Add bin-specific arguments for optimism
if is_optimism {
cmd.arg("--bin")
.arg("op-reth")
.arg("--manifest-path")
.arg("crates/optimism/bin/Cargo.toml");
}
cmd.current_dir(&self.repo_root);
// Set RUSTFLAGS for native CPU optimization
cmd.env("RUSTFLAGS", "-C target-cpu=native");
// Debug log the command
debug!("Executing cargo command: {:?}", cmd);
let output = cmd.output().wrap_err("Failed to execute cargo build command")?;
// Print stdout and stderr with prefixes at debug level
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
for line in stdout.lines() {
if !line.trim().is_empty() {
debug!("[CARGO] {}", line);
}
}
for line in stderr.lines() {
if !line.trim().is_empty() {
debug!("[CARGO] {}", line);
}
}
if !output.status.success() {
// Print all output when compilation fails
error!("Cargo build failed with exit code: {:?}", output.status.code());
if !stdout.trim().is_empty() {
error!("Cargo stdout:");
for line in stdout.lines() {
error!(" {}", line);
}
}
if !stderr.trim().is_empty() {
error!("Cargo stderr:");
for line in stderr.lines() {
error!(" {}", line);
}
}
return Err(eyre!("Compilation failed with exit code: {:?}", output.status.code()));
}
info!("{} compilation completed", binary_name);
// Copy the compiled binary to cache
let source_path =
PathBuf::from(&self.repo_root).join(format!("target/profiling/{}", binary_name));
if !source_path.exists() {
return Err(eyre!("Compiled binary not found at {:?}", source_path));
}
// Create bin directory if it doesn't exist
let bin_dir = self.output_dir.join("bin");
fs::create_dir_all(&bin_dir).wrap_err("Failed to create bin directory")?;
// Copy binary to cache
fs::copy(&source_path, &cached_path).wrap_err("Failed to copy binary to cache")?;
// Make the cached binary executable
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&cached_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&cached_path, perms)?;
}
info!("Cached compiled binary at: {:?}", cached_path);
Ok(())
}
/// Check if reth-bench is available in PATH
pub(crate) fn is_reth_bench_available(&self) -> bool {
match Command::new("which").arg("reth-bench").output() {
Ok(output) => {
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout);
info!("Found reth-bench: {}", path.trim());
true
} else {
false
}
}
Err(_) => false,
}
}
/// Check if samply is available in PATH
pub(crate) fn is_samply_available(&self) -> bool {
match Command::new("which").arg("samply").output() {
Ok(output) => {
if output.status.success() {
let path = String::from_utf8_lossy(&output.stdout);
info!("Found samply: {}", path.trim());
true
} else {
false
}
}
Err(_) => false,
}
}
/// Install samply using cargo
pub(crate) fn install_samply(&self) -> Result<()> {
info!("Installing samply via cargo...");
let mut cmd = Command::new("cargo");
cmd.args(["install", "--locked", "samply"]);
// Debug log the command
debug!("Executing cargo command: {:?}", cmd);
let output = cmd.output().wrap_err("Failed to execute cargo install samply command")?;
// Print stdout and stderr with prefixes at debug level
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
for line in stdout.lines() {
if !line.trim().is_empty() {
debug!("[CARGO-SAMPLY] {}", line);
}
}
for line in stderr.lines() {
if !line.trim().is_empty() {
debug!("[CARGO-SAMPLY] {}", line);
}
}
if !output.status.success() {
// Print all output when installation fails
error!("Cargo install samply failed with exit code: {:?}", output.status.code());
if !stdout.trim().is_empty() {
error!("Cargo stdout:");
for line in stdout.lines() {
error!(" {}", line);
}
}
if !stderr.trim().is_empty() {
error!("Cargo stderr:");
for line in stderr.lines() {
error!(" {}", line);
}
}
return Err(eyre!(
"samply installation failed with exit code: {:?}",
output.status.code()
));
}
info!("Samply installation completed");
Ok(())
}
/// Ensure samply is available, installing if necessary
pub(crate) fn ensure_samply_available(&self) -> Result<()> {
if self.is_samply_available() {
Ok(())
} else {
warn!("samply not found in PATH, installing...");
self.install_samply()
}
}
/// Ensure reth-bench is available, compiling if necessary
pub(crate) fn ensure_reth_bench_available(&self) -> Result<()> {
if self.is_reth_bench_available() {
Ok(())
} else {
warn!("reth-bench not found in PATH, compiling and installing...");
self.compile_reth_bench()
}
}
/// Compile and install reth-bench using `make install-reth-bench`
pub(crate) fn compile_reth_bench(&self) -> Result<()> {
info!("Compiling and installing reth-bench...");
let mut cmd = Command::new("make");
cmd.arg("install-reth-bench").current_dir(&self.repo_root);
// Debug log the command
debug!("Executing make command: {:?}", cmd);
let output = cmd.output().wrap_err("Failed to execute make install-reth-bench command")?;
// Print stdout and stderr with prefixes at debug level
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
for line in stdout.lines() {
if !line.trim().is_empty() {
debug!("[MAKE-BENCH] {}", line);
}
}
for line in stderr.lines() {
if !line.trim().is_empty() {
debug!("[MAKE-BENCH] {}", line);
}
}
if !output.status.success() {
// Print all output when compilation fails
error!("Make install-reth-bench failed with exit code: {:?}", output.status.code());
if !stdout.trim().is_empty() {
error!("Make stdout:");
for line in stdout.lines() {
error!(" {}", line);
}
}
if !stderr.trim().is_empty() {
error!("Make stderr:");
for line in stderr.lines() {
error!(" {}", line);
}
}
return Err(eyre!(
"reth-bench compilation failed with exit code: {:?}",
output.status.code()
));
}
info!("Reth-bench compilation completed");
Ok(())
}
}

View File

@@ -0,0 +1,328 @@
//! Git operations for branch management.
use eyre::{eyre, Result, WrapErr};
use std::process::Command;
use tracing::{info, warn};
/// Manages git operations for branch switching
#[derive(Debug, Clone)]
pub(crate) struct GitManager {
repo_root: String,
}
impl GitManager {
/// Create a new `GitManager`, detecting the repository root
pub(crate) fn new() -> Result<Self> {
let output = Command::new("git")
.args(["rev-parse", "--show-toplevel"])
.output()
.wrap_err("Failed to execute git command - is git installed?")?;
if !output.status.success() {
return Err(eyre!("Not in a git repository or git command failed"));
}
let repo_root = String::from_utf8(output.stdout)
.wrap_err("Git output is not valid UTF-8")?
.trim()
.to_string();
let manager = Self { repo_root };
info!(
"Detected git repository at: {}, current reference: {}",
manager.repo_root(),
manager.get_current_ref()?
);
Ok(manager)
}
/// Get the current git branch name
pub(crate) fn get_current_branch(&self) -> Result<String> {
let output = Command::new("git")
.args(["branch", "--show-current"])
.current_dir(&self.repo_root)
.output()
.wrap_err("Failed to get current branch")?;
if !output.status.success() {
return Err(eyre!("Failed to determine current branch"));
}
let branch = String::from_utf8(output.stdout)
.wrap_err("Branch name is not valid UTF-8")?
.trim()
.to_string();
if branch.is_empty() {
return Err(eyre!("Not on a named branch (detached HEAD?)"));
}
Ok(branch)
}
/// Get the current git reference (branch name, tag, or commit hash)
pub(crate) fn get_current_ref(&self) -> Result<String> {
// First try to get branch name
if let Ok(branch) = self.get_current_branch() {
return Ok(branch);
}
// If not on a branch, check if we're on a tag
let tag_output = Command::new("git")
.args(["describe", "--exact-match", "--tags", "HEAD"])
.current_dir(&self.repo_root)
.output()
.wrap_err("Failed to check for tag")?;
if tag_output.status.success() {
let tag = String::from_utf8(tag_output.stdout)
.wrap_err("Tag name is not valid UTF-8")?
.trim()
.to_string();
return Ok(tag);
}
// If not on a branch or tag, return the commit hash
let commit_output = Command::new("git")
.args(["rev-parse", "HEAD"])
.current_dir(&self.repo_root)
.output()
.wrap_err("Failed to get current commit")?;
if !commit_output.status.success() {
return Err(eyre!("Failed to get current commit hash"));
}
let commit_hash = String::from_utf8(commit_output.stdout)
.wrap_err("Commit hash is not valid UTF-8")?
.trim()
.to_string();
Ok(commit_hash)
}
/// Check if the git working directory has uncommitted changes to tracked files
pub(crate) fn validate_clean_state(&self) -> Result<()> {
let output = Command::new("git")
.args(["status", "--porcelain"])
.current_dir(&self.repo_root)
.output()
.wrap_err("Failed to check git status")?;
if !output.status.success() {
return Err(eyre!("Git status command failed"));
}
let status_output =
String::from_utf8(output.stdout).wrap_err("Git status output is not valid UTF-8")?;
// Check for uncommitted changes to tracked files
// Status codes: M = modified, A = added, D = deleted, R = renamed, C = copied, U = updated
// ?? = untracked files (we want to ignore these)
let has_uncommitted_changes = status_output.lines().any(|line| {
if line.len() >= 2 {
let status = &line[0..2];
// Ignore untracked files (??) and ignored files (!!)
!matches!(status, "??" | "!!")
} else {
false
}
});
if has_uncommitted_changes {
warn!("Git working directory has uncommitted changes to tracked files:");
for line in status_output.lines() {
if line.len() >= 2 && !matches!(&line[0..2], "??" | "!!") {
warn!(" {}", line);
}
}
return Err(eyre!(
"Git working directory has uncommitted changes to tracked files. Please commit or stash changes before running benchmark comparison."
));
}
// Check if there are untracked files and log them as info
let untracked_files: Vec<&str> =
status_output.lines().filter(|line| line.starts_with("??")).collect();
if !untracked_files.is_empty() {
info!(
"Git working directory has {} untracked files (this is OK)",
untracked_files.len()
);
}
info!("Git working directory is clean (no uncommitted changes to tracked files)");
Ok(())
}
/// Fetch all refs from remote to ensure we have latest branches and tags
pub(crate) fn fetch_all(&self) -> Result<()> {
let output = Command::new("git")
.args(["fetch", "--all", "--tags", "--quiet", "--force"])
.current_dir(&self.repo_root)
.output()
.wrap_err("Failed to fetch latest refs")?;
if output.status.success() {
info!("Fetched latest refs");
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
// Only warn if there's actual error content, not just fetch progress
if !stderr.trim().is_empty() && !stderr.contains("-> origin/") {
warn!("Git fetch encountered issues (continuing anyway): {}", stderr);
}
}
Ok(())
}
/// 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 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 found = if let Ok(output) = ref_check &&
output.status.success()
{
info!("Validated reference exists: {}", git_ref);
true
} else {
// 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 (tried '{}' and 'origin/{}^{{commit}}')",
git_ref,
format!("{git_ref}^{{commit}}"),
git_ref,
));
}
}
Ok(())
}
/// Switch to the specified git reference (branch, tag, or commit)
pub(crate) fn switch_ref(&self, git_ref: &str) -> Result<()> {
// First checkout the reference
let output = Command::new("git")
.args(["checkout", git_ref])
.current_dir(&self.repo_root)
.output()
.wrap_err_with(|| format!("Failed to switch to reference '{git_ref}'"))?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(eyre!("Failed to switch to reference '{}': {}", git_ref, stderr));
}
// Check if this is a branch that tracks a remote and pull latest changes
let is_branch = Command::new("git")
.args(["show-ref", "--verify", "--quiet", &format!("refs/heads/{git_ref}")])
.current_dir(&self.repo_root)
.status()
.map(|s| s.success())
.unwrap_or(false);
if is_branch {
// Check if the branch tracks a remote
let tracking_output = Command::new("git")
.args([
"rev-parse",
"--abbrev-ref",
"--symbolic-full-name",
&format!("{git_ref}@{{upstream}}"),
])
.current_dir(&self.repo_root)
.output();
if let Ok(output) = tracking_output &&
output.status.success()
{
let upstream = String::from_utf8_lossy(&output.stdout).trim().to_string();
if !upstream.is_empty() && upstream != format!("{git_ref}@{{upstream}}") {
// Branch tracks a remote, pull latest changes
info!("Pulling latest changes for branch: {}", git_ref);
let pull_output = Command::new("git")
.args(["pull", "--ff-only"])
.current_dir(&self.repo_root)
.output()
.wrap_err_with(|| {
format!("Failed to pull latest changes for branch '{git_ref}'")
})?;
if pull_output.status.success() {
info!("Successfully pulled latest changes for branch: {}", git_ref);
} else {
let stderr = String::from_utf8_lossy(&pull_output.stderr);
warn!("Failed to pull latest changes for branch '{}': {}", git_ref, stderr);
// Continue anyway, we'll use whatever version we have
}
}
}
}
// Verify the checkout succeeded by checking the current commit
let current_commit_output = Command::new("git")
.args(["rev-parse", "HEAD"])
.current_dir(&self.repo_root)
.output()
.wrap_err("Failed to get current commit")?;
if !current_commit_output.status.success() {
return Err(eyre!("Failed to verify git checkout"));
}
info!("Switched to reference: {}", git_ref);
Ok(())
}
/// Get the current commit hash
pub(crate) fn get_current_commit(&self) -> Result<String> {
let output = Command::new("git")
.args(["rev-parse", "HEAD"])
.current_dir(&self.repo_root)
.output()
.wrap_err("Failed to get current commit")?;
if !output.status.success() {
return Err(eyre!("Failed to get current commit hash"));
}
let commit_hash = String::from_utf8(output.stdout)
.wrap_err("Commit hash is not valid UTF-8")?
.trim()
.to_string();
Ok(commit_hash)
}
/// Get the repository root path
pub(crate) fn repo_root(&self) -> &str {
&self.repo_root
}
}

View File

@@ -0,0 +1,45 @@
//! # reth-bench-compare
//!
//! Automated tool for comparing reth performance between two git branches.
//! This tool automates the complete workflow of compiling, running, and benchmarking
//! reth on different branches to provide meaningful performance comparisons.
#![doc(
html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png",
html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256",
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#[global_allocator]
static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator();
mod benchmark;
mod cli;
mod comparison;
mod compilation;
mod git;
mod node;
use clap::Parser;
use cli::{run_comparison, Args};
use eyre::Result;
use reth_cli_runner::CliRunner;
fn main() -> Result<()> {
// Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided.
if std::env::var_os("RUST_BACKTRACE").is_none() {
unsafe {
std::env::set_var("RUST_BACKTRACE", "1");
}
}
let args = Args::parse();
// Initialize tracing
let _guard = args.init_tracing()?;
// Run until either exit or sigint or sigterm
let runner = CliRunner::try_default_runtime()?;
runner.run_command_until_exit(|ctx| run_comparison(args, ctx))
}

View File

@@ -0,0 +1,554 @@
//! Node management for starting, stopping, and controlling reth instances.
use crate::cli::Args;
use alloy_provider::{Provider, ProviderBuilder};
use alloy_rpc_types_eth::SyncStatus;
use eyre::{eyre, OptionExt, Result, WrapErr};
#[cfg(unix)]
use nix::sys::signal::{killpg, Signal};
#[cfg(unix)]
use nix::unistd::Pid;
use reth_chainspec::Chain;
use std::{fs, path::PathBuf, time::Duration};
use tokio::{
fs::File as AsyncFile,
io::{AsyncBufReadExt, AsyncWriteExt, BufReader as AsyncBufReader},
process::Command,
time::{sleep, timeout},
};
use tracing::{debug, info, warn};
/// Manages reth node lifecycle and operations
pub(crate) struct NodeManager {
datadir: Option<String>,
metrics_port: u16,
chain: Chain,
use_sudo: bool,
binary_path: Option<std::path::PathBuf>,
enable_profiling: bool,
output_dir: PathBuf,
additional_reth_args: Vec<String>,
comparison_dir: Option<PathBuf>,
tracing_endpoint: Option<String>,
otlp_max_queue_size: usize,
}
impl NodeManager {
/// Create a new `NodeManager` with configuration from CLI args
pub(crate) fn new(args: &Args) -> Self {
Self {
datadir: Some(args.datadir_path().to_string_lossy().to_string()),
metrics_port: args.metrics_port,
chain: args.chain,
use_sudo: args.sudo,
binary_path: None,
enable_profiling: args.profile,
output_dir: args.output_dir_path(),
// 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,
}
}
/// Set the comparison directory path for logging
pub(crate) fn set_comparison_dir(&mut self, dir: PathBuf) {
self.comparison_dir = Some(dir);
}
/// Get the log file path for a given reference type
fn get_log_file_path(&self, ref_type: &str) -> Result<PathBuf> {
let comparison_dir = self
.comparison_dir
.as_ref()
.ok_or_eyre("Comparison directory not set. Call set_comparison_dir first.")?;
// The comparison directory already contains the full path to results/<timestamp>
let log_dir = comparison_dir.join(ref_type);
// Create the directory if it doesn't exist
fs::create_dir_all(&log_dir)
.wrap_err(format!("Failed to create log directory: {:?}", log_dir))?;
let log_file = log_dir.join("reth_node.log");
Ok(log_file)
}
/// Get the perf event max sample rate from the system, capped at 10000
fn get_perf_sample_rate(&self) -> Option<String> {
let perf_rate_file = "/proc/sys/kernel/perf_event_max_sample_rate";
if let Ok(content) = fs::read_to_string(perf_rate_file) {
let rate_str = content.trim();
if !rate_str.is_empty() {
if let Ok(system_rate) = rate_str.parse::<u32>() {
let capped_rate = std::cmp::min(system_rate, 10000);
info!(
"Detected perf_event_max_sample_rate: {}, using: {}",
system_rate, capped_rate
);
return Some(capped_rate.to_string());
}
warn!("Failed to parse perf_event_max_sample_rate: {}", rate_str);
}
}
None
}
/// Get the absolute path to samply using 'which' command
async fn get_samply_path(&self) -> Result<String> {
let output = Command::new("which")
.arg("samply")
.output()
.await
.wrap_err("Failed to execute 'which samply' command")?;
if !output.status.success() {
return Err(eyre!("samply not found in PATH"));
}
let samply_path = String::from_utf8(output.stdout)
.wrap_err("samply path is not valid UTF-8")?
.trim()
.to_string();
if samply_path.is_empty() {
return Err(eyre!("which samply returned empty path"));
}
Ok(samply_path)
}
/// Build reth arguments as a vector of strings
fn build_reth_args(
&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()];
// Add chain argument (skip for mainnet as it's the default)
let chain_str = self.chain.to_string();
if chain_str != "mainnet" {
reth_args.extend_from_slice(&["--chain".to_string(), chain_str.clone()]);
}
// Add datadir if specified
if let Some(ref datadir) = self.datadir {
reth_args.extend_from_slice(&["--datadir".to_string(), datadir.clone()]);
}
// Add reth-specific arguments
let metrics_arg = format!("0.0.0.0:{}", self.metrics_port);
reth_args.extend_from_slice(&[
"--engine.accept-execution-requests-hash".to_string(),
"--metrics".to_string(),
metrics_arg,
"--http".to_string(),
"--http.api".to_string(),
"eth".to_string(),
"--disable-discovery".to_string(),
"--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);
// Add reference-specific additional arguments
reth_args.extend_from_slice(additional_args);
(reth_args, chain_str)
}
/// Create a command for profiling mode
async fn create_profiling_command(
&self,
ref_type: &str,
reth_args: &[String],
) -> Result<Command> {
// Create profiles directory if it doesn't exist
let profile_dir = self.output_dir.join("profiles");
fs::create_dir_all(&profile_dir).wrap_err("Failed to create profiles directory")?;
let profile_path = profile_dir.join(format!("{}.json.gz", ref_type));
info!("Starting reth node with samply profiling...");
info!("Profile output: {:?}", profile_path);
// Get absolute path to samply
let samply_path = self.get_samply_path().await?;
let mut cmd = if self.use_sudo {
let mut sudo_cmd = Command::new("sudo");
sudo_cmd.arg(&samply_path);
sudo_cmd
} else {
Command::new(&samply_path)
};
// Add samply arguments
cmd.args(["record", "--save-only", "-o", &profile_path.to_string_lossy()]);
// Add rate argument if available
if let Some(rate) = self.get_perf_sample_rate() {
cmd.args(["--rate", &rate]);
}
// Add separator and complete reth command
cmd.arg("--");
cmd.args(reth_args);
// Set environment variable to disable log styling
cmd.env("RUST_LOG_STYLE", "never");
Ok(cmd)
}
/// Create a command for direct reth execution
fn create_direct_command(&self, reth_args: &[String]) -> Command {
let binary_path = &reth_args[0];
let mut cmd = if self.use_sudo {
info!("Starting reth node with sudo...");
let mut sudo_cmd = Command::new("sudo");
sudo_cmd.args(reth_args);
sudo_cmd
} else {
info!("Starting reth node...");
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, 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, 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() {
info!("Using common additional reth arguments: {:?}", self.additional_reth_args);
}
if !additional_args.is_empty() {
info!("Using reference-specific additional reth arguments: {:?}", additional_args);
}
let mut cmd = if self.enable_profiling {
self.create_profiling_command(ref_type, &reth_args).await?
} else {
self.create_direct_command(&reth_args)
};
// Set process group for better signal handling
#[cfg(unix)]
{
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
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.kill_on_drop(true) // Kill on drop so that on Ctrl-C for parent process we stop all child processes
.spawn()
.wrap_err("Failed to start reth node")?;
info!(
"Reth node started with PID: {:?} (binary: {})",
child.id().ok_or_eyre("Reth node is not running")?,
binary_path_str
);
// Prepare log file path
let log_file_path = self.get_log_file_path(ref_type)?;
info!("Reth node logs will be saved to: {:?}", log_file_path);
// Stream stdout and stderr with prefixes at debug level and to log file
if let Some(stdout) = child.stdout.take() {
let log_file = AsyncFile::create(&log_file_path)
.await
.wrap_err(format!("Failed to create log file: {:?}", log_file_path))?;
tokio::spawn(async move {
let reader = AsyncBufReader::new(stdout);
let mut lines = reader.lines();
let mut log_file = log_file;
while let Ok(Some(line)) = lines.next_line().await {
debug!("[RETH] {}", line);
// Write to log file (reth already includes timestamps)
let log_line = format!("{}\n", line);
if let Err(e) = log_file.write_all(log_line.as_bytes()).await {
debug!("Failed to write to log file: {}", e);
}
}
});
}
if let Some(stderr) = child.stderr.take() {
let log_file = AsyncFile::options()
.create(true)
.append(true)
.open(&log_file_path)
.await
.wrap_err(format!("Failed to open log file for stderr: {:?}", log_file_path))?;
tokio::spawn(async move {
let reader = AsyncBufReader::new(stderr);
let mut lines = reader.lines();
let mut log_file = log_file;
while let Ok(Some(line)) = lines.next_line().await {
debug!("[RETH] {}", line);
// Write to log file (reth already includes timestamps)
let log_line = format!("{}\n", line);
if let Err(e) = log_file.write_all(log_line.as_bytes()).await {
debug!("Failed to write to log file: {}", e);
}
}
});
}
// Give the node a moment to start up
sleep(Duration::from_secs(5)).await;
Ok((child, reth_command))
}
/// Wait for the node to be ready and return its current tip
pub(crate) async fn wait_for_node_ready_and_get_tip(&self) -> Result<u64> {
info!("Waiting for node to be ready and synced...");
let max_wait = Duration::from_secs(120); // 2 minutes to allow for sync
let check_interval = Duration::from_secs(2);
let rpc_url = "http://localhost:8545";
// Create Alloy provider
let url = rpc_url.parse().map_err(|e| eyre!("Invalid RPC URL '{}': {}", rpc_url, e))?;
let provider = ProviderBuilder::new().connect_http(url);
timeout(max_wait, async {
loop {
// First check if RPC is up and node is not syncing
match provider.syncing().await {
Ok(sync_result) => {
match sync_result {
SyncStatus::Info(sync_info) => {
debug!("Node is still syncing {sync_info:?}, waiting...");
}
_ => {
// Node is not syncing, now get the tip
match provider.get_block_number().await {
Ok(tip) => {
info!("Node is ready and not syncing at block: {}", tip);
return Ok(tip);
}
Err(e) => {
debug!("Failed to get block number: {}", e);
}
}
}
}
}
Err(e) => {
debug!("Node RPC not ready yet or failed to check sync status: {}", e);
}
}
sleep(check_interval).await;
}
})
.await
.wrap_err("Timed out waiting for node to be ready and synced")?
}
/// Stop the reth node gracefully
pub(crate) async fn stop_node(&self, child: &mut tokio::process::Child) -> Result<()> {
let pid = child.id().expect("Child process ID should be available");
// Check if the process has already exited
match child.try_wait() {
Ok(Some(status)) => {
info!("Reth node (PID: {}) has already exited with status: {:?}", pid, status);
return Ok(());
}
Ok(None) => {
// Process is still running, proceed to stop it
info!("Stopping process gracefully with SIGINT (PID: {})...", pid);
}
Err(e) => {
return Err(eyre!("Failed to check process status: {}", e));
}
}
#[cfg(unix)]
{
// Send SIGINT to process group to mimic Ctrl-C behavior
let nix_pgid = Pid::from_raw(pid as i32);
match killpg(nix_pgid, Signal::SIGINT) {
Ok(()) => {}
Err(nix::errno::Errno::ESRCH) => {
info!("Process group {} has already exited", pid);
}
Err(e) => {
return Err(eyre!("Failed to send SIGINT to process group {}: {}", pid, e));
}
}
}
#[cfg(not(unix))]
{
// On non-Unix systems, fall back to using external kill command
let output = Command::new("taskkill")
.args(["/PID", &pid.to_string(), "/F"])
.output()
.await
.wrap_err("Failed to execute taskkill command")?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
// Check if the error is because the process doesn't exist
if stderr.contains("not found") || stderr.contains("not exist") {
info!("Process {} has already exited", pid);
} else {
return Err(eyre!("Failed to kill process {}: {}", pid, stderr));
}
}
}
// Wait for the process to exit
match child.wait().await {
Ok(status) => {
info!("Reth node (PID: {}) exited with status: {:?}", pid, status);
}
Err(e) => {
// If we get an error here, it might be because the process already exited
debug!("Error waiting for process exit (may have already exited): {}", e);
}
}
Ok(())
}
/// Unwind the node to a specific block
pub(crate) async fn unwind_to_block(&self, block_number: u64) -> Result<()> {
if self.use_sudo {
info!("Unwinding node to block: {} (with sudo)", block_number);
} else {
info!("Unwinding node to block: {}", block_number);
}
// Use the binary path from the last start_node call, or fallback to default
let binary_path = self
.binary_path
.as_ref()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|| "./target/profiling/reth".to_string());
let mut cmd = if self.use_sudo {
let mut sudo_cmd = Command::new("sudo");
sudo_cmd.args([&binary_path, "stage", "unwind"]);
sudo_cmd
} else {
let mut reth_cmd = Command::new(&binary_path);
reth_cmd.args(["stage", "unwind"]);
reth_cmd
};
// Add chain argument (skip for mainnet as it's the default)
let chain_str = self.chain.to_string();
if chain_str != "mainnet" {
cmd.args(["--chain", &chain_str]);
}
// Add datadir if specified
if let Some(ref datadir) = self.datadir {
cmd.args(["--datadir", datadir]);
}
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);
let mut child = cmd
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.wrap_err("Failed to start unwind command")?;
// Stream stdout and stderr with prefixes in real-time
if let Some(stdout) = child.stdout.take() {
tokio::spawn(async move {
let reader = AsyncBufReader::new(stdout);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
debug!("[RETH-UNWIND] {}", line);
}
});
}
if let Some(stderr) = child.stderr.take() {
tokio::spawn(async move {
let reader = AsyncBufReader::new(stderr);
let mut lines = reader.lines();
while let Ok(Some(line)) = lines.next_line().await {
debug!("[RETH-UNWIND] {}", line);
}
});
}
// Wait for the command to complete
let status = child.wait().await.wrap_err("Failed to wait for unwind command")?;
if !status.success() {
return Err(eyre!("Unwind command failed with exit code: {:?}", status.code()));
}
info!("Unwound to block: {}", block_number);
Ok(())
}
}

View File

@@ -81,11 +81,26 @@ jemalloc = [
jemalloc-prof = ["reth-cli-util/jemalloc-prof"]
tracy-allocator = ["reth-cli-util/tracy-allocator"]
min-error-logs = ["tracing/release_max_level_error"]
min-warn-logs = ["tracing/release_max_level_warn"]
min-info-logs = ["tracing/release_max_level_info"]
min-debug-logs = ["tracing/release_max_level_debug"]
min-trace-logs = ["tracing/release_max_level_trace"]
min-error-logs = [
"tracing/release_max_level_error",
"reth-node-core/min-error-logs",
]
min-warn-logs = [
"tracing/release_max_level_warn",
"reth-node-core/min-warn-logs",
]
min-info-logs = [
"tracing/release_max_level_info",
"reth-node-core/min-info-logs",
]
min-debug-logs = [
"tracing/release_max_level_debug",
"reth-node-core/min-debug-logs",
]
min-trace-logs = [
"tracing/release_max_level_trace",
"reth-node-core/min-trace-logs",
]
# no-op feature flag for switching between the `optimism` and default functionality in CI matrices
ethereum = []

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

@@ -7,6 +7,7 @@ use alloy_primitives::address;
use alloy_provider::{network::AnyNetwork, Provider, RootProvider};
use alloy_rpc_client::ClientBuilder;
use alloy_rpc_types_engine::JwtSecret;
use alloy_transport::layers::RetryBackoffLayer;
use reqwest::Url;
use reth_node_core::args::BenchmarkArgs;
use tracing::info;
@@ -49,7 +50,9 @@ impl BenchContext {
}
// set up alloy client for blocks
let client = ClientBuilder::default().http(rpc_url.parse()?);
let client = ClientBuilder::default()
.layer(RetryBackoffLayer::new(10, 800, u64::MAX))
.http(rpc_url.parse()?);
let block_provider = RootProvider::<AnyNetwork>::new(client);
// Check if this is an OP chain by checking code at a predeploy address.

View File

@@ -15,7 +15,7 @@ use alloy_provider::Provider;
use alloy_rpc_types_engine::ForkchoiceState;
use clap::Parser;
use csv::Writer;
use eyre::Context;
use eyre::{Context, OptionExt};
use humantime::parse_duration;
use reth_cli_runner::CliContext;
use reth_node_core::args::BenchmarkArgs;
@@ -30,8 +30,18 @@ pub struct Command {
rpc_url: String,
/// How long to wait after a forkchoice update before sending the next payload.
#[arg(long, value_name = "WAIT_TIME", value_parser = parse_duration, verbatim_doc_comment)]
wait_time: Option<Duration>,
#[arg(long, value_name = "WAIT_TIME", value_parser = parse_duration, default_value = "250ms", verbatim_doc_comment)]
wait_time: Duration,
/// The size of the block buffer (channel capacity) for prefetching blocks from the RPC
/// endpoint.
#[arg(
long = "rpc-block-buffer-size",
value_name = "RPC_BLOCK_BUFFER_SIZE",
default_value = "20",
verbatim_doc_comment
)]
rpc_block_buffer_size: usize,
#[command(flatten)]
benchmark: BenchmarkArgs,
@@ -48,7 +58,12 @@ impl Command {
is_optimism,
} = BenchContext::new(&self.benchmark, self.rpc_url).await?;
let (sender, mut receiver) = tokio::sync::mpsc::channel(1000);
let buffer_size = self.rpc_block_buffer_size;
// Use a oneshot channel to propagate errors from the spawned task
let (error_sender, mut error_receiver) = tokio::sync::oneshot::channel();
let (sender, mut receiver) = tokio::sync::mpsc::channel(buffer_size);
tokio::task::spawn(async move {
while benchmark_mode.contains(next_block) {
let block_res = block_provider
@@ -56,35 +71,42 @@ impl Command {
.full()
.await
.wrap_err_with(|| format!("Failed to fetch block by number {next_block}"));
let block = block_res.unwrap().unwrap();
let header = block.header.clone();
let block = match block_res.and_then(|opt| opt.ok_or_eyre("Block not found")) {
Ok(block) => block,
Err(e) => {
tracing::error!("Failed to fetch block {next_block}: {e}");
let _ = error_sender.send(e);
break;
}
};
let (version, params) = block_to_new_payload(block, is_optimism).unwrap();
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,);
let safe_block_hash = safe.unwrap().expect("finalized block exists").header.hash;
let finalized_block_hash =
finalized.unwrap().expect("finalized block exists").header.hash;
let safe_block_hash = match safe {
Ok(Some(block)) => block.header.hash,
Ok(None) | Err(_) => head_block_hash,
};
let finalized_block_hash = match finalized {
Ok(Some(block)) => block.header.hash,
Ok(None) | Err(_) => head_block_hash,
};
next_block += 1;
sender
.send((
header,
version,
params,
head_block_hash,
safe_block_hash,
finalized_block_hash,
))
if let Err(e) = sender
.send((block, head_block_hash, safe_block_hash, finalized_block_hash))
.await
.unwrap();
{
tracing::error!("Failed to send block data: {e}");
break;
}
}
});
@@ -93,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",);
@@ -112,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?;
@@ -122,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
@@ -132,16 +161,20 @@ impl Command {
// convert gas used to gigagas, then compute gigagas per second
info!(%combined_result);
// wait if we need to
if let Some(wait_time) = self.wait_time {
tokio::time::sleep(wait_time).await;
}
// wait before sending the next payload
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));
}
// Check if the spawned task encountered an error
if let Ok(error) = error_receiver.try_recv() {
return Err(error);
}
let (gas_output_results, combined_results): (_, Vec<CombinedResult>) =
results.into_iter().unzip();
@@ -169,7 +202,7 @@ impl Command {
}
// accumulate the results and calculate the overall Ggas/s
let gas_output = TotalGasOutput::new(gas_output_results);
let gas_output = TotalGasOutput::new(gas_output_results)?;
info!(
total_duration=?gas_output.total_duration,
total_gas_used=?gas_output.total_gas_used,

View File

@@ -13,7 +13,7 @@ use crate::{
use alloy_provider::Provider;
use clap::Parser;
use csv::Writer;
use eyre::Context;
use eyre::{Context, OptionExt};
use reth_cli_runner::CliContext;
use reth_node_core::args::BenchmarkArgs;
use std::time::{Duration, Instant};
@@ -26,6 +26,16 @@ pub struct Command {
#[arg(long, value_name = "RPC_URL", verbatim_doc_comment)]
rpc_url: String,
/// The size of the block buffer (channel capacity) for prefetching blocks from the RPC
/// endpoint.
#[arg(
long = "rpc-block-buffer-size",
value_name = "RPC_BLOCK_BUFFER_SIZE",
default_value = "20",
verbatim_doc_comment
)]
rpc_block_buffer_size: usize,
#[command(flatten)]
benchmark: BenchmarkArgs,
}
@@ -41,7 +51,12 @@ impl Command {
is_optimism,
} = BenchContext::new(&self.benchmark, self.rpc_url).await?;
let (sender, mut receiver) = tokio::sync::mpsc::channel(1000);
let buffer_size = self.rpc_block_buffer_size;
// Use a oneshot channel to propagate errors from the spawned task
let (error_sender, mut error_receiver) = tokio::sync::oneshot::channel();
let (sender, mut receiver) = tokio::sync::mpsc::channel(buffer_size);
tokio::task::spawn(async move {
while benchmark_mode.contains(next_block) {
let block_res = block_provider
@@ -49,13 +64,20 @@ impl Command {
.full()
.await
.wrap_err_with(|| format!("Failed to fetch block by number {next_block}"));
let block = block_res.unwrap().unwrap();
let header = block.header.clone();
let (version, params) = block_to_new_payload(block, is_optimism).unwrap();
let block = match block_res.and_then(|opt| opt.ok_or_eyre("Block not found")) {
Ok(block) => block,
Err(e) => {
tracing::error!("Failed to fetch block {next_block}: {e}");
let _ = error_sender.send(e);
break;
}
};
next_block += 1;
sender.send((header, version, params)).await.unwrap();
if let Err(e) = sender.send(block).await {
tracing::error!("Failed to send block data: {e}");
break;
}
}
});
@@ -64,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?;
@@ -92,10 +115,16 @@ 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));
}
// Check if the spawned task encountered an error
if let Ok(error) = error_receiver.try_recv() {
return Err(error);
}
let (gas_output_results, new_payload_results): (_, Vec<NewPayloadResult>) =
results.into_iter().unzip();
@@ -123,7 +152,7 @@ impl Command {
}
// accumulate the results and calculate the overall Ggas/s
let gas_output = TotalGasOutput::new(gas_output_results);
let gas_output = TotalGasOutput::new(gas_output_results)?;
info!(
total_duration=?gas_output.total_duration,
total_gas_used=?gas_output.total_gas_used,

View File

@@ -1,6 +1,7 @@
//! Contains various benchmark output formats, either for logging or for
//! serialization to / from files.
use eyre::OptionExt;
use reth_primitives_traits::constants::GIGAGAS;
use serde::{ser::SerializeStruct, Serialize};
use std::time::Duration;
@@ -66,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.
@@ -107,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)?;
@@ -124,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.
@@ -145,15 +151,14 @@ pub(crate) struct TotalGasOutput {
impl TotalGasOutput {
/// Create a new [`TotalGasOutput`] from a list of [`TotalGasRow`].
pub(crate) fn new(rows: Vec<TotalGasRow>) -> Self {
pub(crate) fn new(rows: Vec<TotalGasRow>) -> eyre::Result<Self> {
// the duration is obtained from the last row
let total_duration =
rows.last().map(|row| row.time).expect("the row has at least one element");
let total_duration = rows.last().map(|row| row.time).ok_or_eyre("empty results")?;
let blocks_processed = rows.len() as u64;
let total_gas_used: u64 = rows.into_iter().map(|row| row.gas_used).sum();
let total_gas_per_second = total_gas_used as f64 / total_duration.as_secs_f64();
Self { total_gas_used, total_duration, total_gas_per_second, blocks_processed }
Ok(Self { total_gas_used, total_duration, total_gas_per_second, blocks_processed })
}
/// Return the total gigagas per second.
@@ -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,7 +9,7 @@
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[global_allocator]
static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator();
@@ -26,7 +26,9 @@ use reth_cli_runner::CliRunner;
fn main() {
// Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided.
if std::env::var_os("RUST_BACKTRACE").is_none() {
std::env::set_var("RUST_BACKTRACE", "1");
unsafe {
std::env::set_var("RUST_BACKTRACE", "1");
}
}
// Run until either exit or sigint or sigterm

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
@@ -40,7 +54,7 @@ reth-node-api.workspace = true
reth-node-core.workspace = true
reth-ethereum-payload-builder.workspace = true
reth-ethereum-primitives.workspace = true
reth-node-ethereum = { workspace = true, features = ["js-tracer"] }
reth-node-ethereum.workspace = true
reth-node-builder.workspace = true
reth-node-metrics.workspace = true
reth-consensus.workspace = true
@@ -67,7 +81,18 @@ backon.workspace = true
tempfile.workspace = true
[features]
default = ["jemalloc", "reth-revm/portable"]
default = ["jemalloc", "otlp", "reth-revm/portable", "js-tracer", "keccak-cache-global"]
otlp = [
"reth-ethereum-cli/otlp",
"reth-node-core/otlp",
]
js-tracer = [
"reth-node-builder/js-tracer",
"reth-node-ethereum/js-tracer",
"reth-rpc/js-tracer",
"reth-rpc-eth-types/js-tracer",
]
dev = ["reth-ethereum-cli/dev"]
@@ -77,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",
@@ -89,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",
@@ -109,22 +142,27 @@ snmalloc-native = [
min-error-logs = [
"tracing/release_max_level_error",
"reth-ethereum-cli/min-error-logs",
"reth-node-core/min-error-logs",
]
min-warn-logs = [
"tracing/release_max_level_warn",
"reth-ethereum-cli/min-warn-logs",
"reth-node-core/min-warn-logs",
]
min-info-logs = [
"tracing/release_max_level_info",
"reth-ethereum-cli/min-info-logs",
"reth-node-core/min-info-logs",
]
min-debug-logs = [
"tracing/release_max_level_debug",
"reth-ethereum-cli/min-debug-logs",
"reth-node-core/min-debug-logs",
]
min-trace-logs = [
"tracing/release_max_level_trace",
"reth-ethereum-cli/min-trace-logs",
"reth-node-core/min-trace-logs",
]
[[bin]]

View File

@@ -25,7 +25,7 @@
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod cli;

View File

@@ -54,6 +54,8 @@ reth-testing-utils.workspace = true
alloy-signer.workspace = true
alloy-signer-local.workspace = true
rand.workspace = true
revm-state.workspace = true
criterion.workspace = true
[features]
serde = [
@@ -82,3 +84,8 @@ test-utils = [
"reth-trie/test-utils",
"reth-ethereum-primitives/test-utils",
]
[[bench]]
name = "canonical_hashes_range"
harness = false
required-features = ["test-utils"]

View File

@@ -0,0 +1,96 @@
#![allow(missing_docs)]
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use reth_chain_state::{
test_utils::TestBlockBuilder, ExecutedBlock, MemoryOverlayStateProviderRef,
};
use reth_ethereum_primitives::EthPrimitives;
use reth_storage_api::{noop::NoopProvider, BlockHashReader};
criterion_group!(benches, bench_canonical_hashes_range);
criterion_main!(benches);
fn bench_canonical_hashes_range(c: &mut Criterion) {
let mut group = c.benchmark_group("canonical_hashes_range");
let scenarios = [("small", 10), ("medium", 100), ("large", 1000)];
for (name, num_blocks) in scenarios {
group.bench_function(format!("{}_blocks_{}", name, num_blocks), |b| {
let (provider, blocks) = setup_provider_with_blocks(num_blocks);
let start_block = blocks[0].recovered_block().number;
let end_block = blocks[num_blocks / 2].recovered_block().number;
b.iter(|| {
black_box(
provider
.canonical_hashes_range(black_box(start_block), black_box(end_block))
.unwrap(),
)
})
});
}
let (provider, blocks) = setup_provider_with_blocks(500);
let base_block = blocks[100].recovered_block().number;
let range_sizes = [1, 10, 50, 100, 250];
for range_size in range_sizes {
group.bench_function(format!("range_size_{}", range_size), |b| {
let end_block = base_block + range_size;
b.iter(|| {
black_box(
provider
.canonical_hashes_range(black_box(base_block), black_box(end_block))
.unwrap(),
)
})
});
}
// Benchmark edge cases
group.bench_function("no_in_memory_matches", |b| {
let (provider, blocks) = setup_provider_with_blocks(100);
let first_block = blocks[0].recovered_block().number;
let start_block = first_block - 50;
let end_block = first_block - 10;
b.iter(|| {
black_box(
provider
.canonical_hashes_range(black_box(start_block), black_box(end_block))
.unwrap(),
)
})
});
group.bench_function("all_in_memory_matches", |b| {
let (provider, blocks) = setup_provider_with_blocks(100);
let first_block = blocks[0].recovered_block().number;
let last_block = blocks[blocks.len() - 1].recovered_block().number;
b.iter(|| {
black_box(
provider
.canonical_hashes_range(black_box(first_block), black_box(last_block + 1))
.unwrap(),
)
})
});
group.finish();
}
fn setup_provider_with_blocks(
num_blocks: usize,
) -> (MemoryOverlayStateProviderRef<'static, EthPrimitives>, Vec<ExecutedBlock<EthPrimitives>>) {
let mut builder = TestBlockBuilder::<EthPrimitives>::default();
let blocks: Vec<_> = builder.get_executed_blocks(1000..1000 + num_blocks as u64).collect();
let historical = Box::new(NoopProvider::default());
let provider = MemoryOverlayStateProviderRef::new(historical, blocks.clone());
(provider, blocks)
}

View File

@@ -77,22 +77,22 @@ where
self.inner.finalized_block.borrow().clone()
}
/// Returns the canonical head of the chain.
/// Returns the `BlockNumHash` of the canonical head.
pub fn get_canonical_num_hash(&self) -> BlockNumHash {
self.inner.canonical_head.read().num_hash()
}
/// Returns the canonical head of the chain.
/// Returns the block number of the canonical head.
pub fn get_canonical_block_number(&self) -> BlockNumber {
self.inner.canonical_head_number.load(Ordering::Relaxed)
}
/// Returns the safe header of the chain.
/// Returns the `BlockNumHash` of the safe header.
pub fn get_safe_num_hash(&self) -> Option<BlockNumHash> {
self.inner.safe_block.borrow().as_ref().map(SealedHeader::num_hash)
}
/// Returns the finalized header of the chain.
/// Returns the `BlockNumHash` of the finalized header.
pub fn get_finalized_num_hash(&self) -> Option<BlockNumHash> {
self.inner.finalized_block.borrow().as_ref().map(SealedHeader::num_hash)
}

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.
@@ -242,7 +242,7 @@ impl<N: NodePrimitives> CanonicalInMemoryState<N> {
/// Updates the pending block with the given block.
///
/// Note: This assumes that the parent block of the pending block is canonical.
pub fn set_pending_block(&self, pending: ExecutedBlockWithTrieUpdates<N>) {
pub fn set_pending_block(&self, pending: ExecutedBlock<N>) {
// fetch the state of the pending block's parent block
let parent = self.state_by_hash(pending.recovered_block().parent_hash());
let pending = BlockState::with_parent(pending, parent);
@@ -258,7 +258,7 @@ impl<N: NodePrimitives> CanonicalInMemoryState<N> {
/// them to their parent blocks.
fn update_blocks<I, R>(&self, new_blocks: I, reorged: R)
where
I: IntoIterator<Item = ExecutedBlockWithTrieUpdates<N>>,
I: IntoIterator<Item = ExecutedBlock<N>>,
R: IntoIterator<Item = ExecutedBlock<N>>,
{
{
@@ -565,25 +565,28 @@ 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: ExecutedBlockWithTrieUpdates<N>,
block: ExecutedBlock<N>,
/// The block's parent block if it exists.
parent: Option<Arc<BlockState<N>>>,
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: ExecutedBlockWithTrieUpdates<N>) -> Self {
pub const fn new(block: ExecutedBlock<N>) -> Self {
Self { block, parent: None }
}
/// [`BlockState`] constructor with parent.
pub const fn with_parent(
block: ExecutedBlockWithTrieUpdates<N>,
parent: Option<Arc<Self>>,
) -> Self {
pub const fn with_parent(block: ExecutedBlock<N>, parent: Option<Arc<Self>>) -> Self {
Self { block, parent }
}
@@ -597,12 +600,12 @@ impl<N: NodePrimitives> BlockState<N> {
}
/// Returns the executed block that determines the state.
pub fn block(&self) -> ExecutedBlockWithTrieUpdates<N> {
pub fn block(&self) -> ExecutedBlock<N> {
self.block.clone()
}
/// Returns a reference to the executed block that determines the state.
pub const fn block_ref(&self) -> &ExecutedBlockWithTrieUpdates<N> {
pub const fn block_ref(&self) -> &ExecutedBlock<N> {
&self.block
}
@@ -631,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();
@@ -643,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):
@@ -722,14 +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>,
/// 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> {
@@ -737,12 +761,54 @@ impl<N: NodePrimitives> Default for ExecutedBlock<N> {
Self {
recovered_block: Default::default(),
execution_output: Default::default(),
hashed_state: 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,10 +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 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_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.
@@ -774,127 +885,20 @@ impl<N: NodePrimitives> ExecutedBlock<N> {
}
}
/// Trie updates that result from calculating the state root for the block.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ExecutedTrieUpdates {
/// Trie updates present. State root was calculated, and the trie updates can be applied to the
/// database.
Present(Arc<TrieUpdates>),
/// Trie updates missing. State root was calculated, but the trie updates cannot be applied to
/// the current database state. To apply the updates, the state root must be recalculated, and
/// new trie updates must be generated.
///
/// This can happen when processing fork chain blocks that are building on top of the
/// historical database state. Since we don't store the historical trie state, we cannot
/// generate the trie updates for it.
Missing,
}
impl ExecutedTrieUpdates {
/// Creates a [`ExecutedTrieUpdates`] with present but empty trie updates.
pub fn empty() -> Self {
Self::Present(Arc::default())
}
/// Sets the trie updates to the provided value as present.
pub fn set_present(&mut self, updates: Arc<TrieUpdates>) {
*self = Self::Present(updates);
}
/// Takes the present trie updates, leaving the state as missing.
pub fn take_present(&mut self) -> Option<Arc<TrieUpdates>> {
match self {
Self::Present(updates) => {
let updates = core::mem::take(updates);
*self = Self::Missing;
Some(updates)
}
Self::Missing => None,
}
}
/// Returns a reference to the trie updates if present.
#[allow(clippy::missing_const_for_fn)] // false positive
pub fn as_ref(&self) -> Option<&TrieUpdates> {
match self {
Self::Present(updates) => Some(updates),
Self::Missing => None,
}
}
/// Returns `true` if the trie updates are present.
pub const fn is_present(&self) -> bool {
matches!(self, Self::Present(_))
}
/// Returns `true` if the trie updates are missing.
pub const fn is_missing(&self) -> bool {
matches!(self, Self::Missing)
}
}
/// An [`ExecutedBlock`] with its [`TrieUpdates`].
///
/// We store it as separate type because [`TrieUpdates`] are only available for blocks stored in
/// memory and can't be obtained for canonical persisted blocks.
#[derive(
Clone, Debug, PartialEq, Eq, derive_more::Deref, derive_more::DerefMut, derive_more::Into,
)]
pub struct ExecutedBlockWithTrieUpdates<N: NodePrimitives = EthPrimitives> {
/// Inner [`ExecutedBlock`].
#[deref]
#[deref_mut]
#[into]
pub block: ExecutedBlock<N>,
/// Trie updates that result from calculating the state root for the block.
///
/// If [`ExecutedTrieUpdates::Missing`], the trie updates should be computed when persisting
/// the block **on top of the canonical parent**.
pub trie: ExecutedTrieUpdates,
}
impl<N: NodePrimitives> ExecutedBlockWithTrieUpdates<N> {
/// [`ExecutedBlock`] constructor.
pub const fn new(
recovered_block: Arc<RecoveredBlock<N::Block>>,
execution_output: Arc<ExecutionOutcome<N::Receipt>>,
hashed_state: Arc<HashedPostState>,
trie: ExecutedTrieUpdates,
) -> Self {
Self { block: ExecutedBlock { recovered_block, execution_output, hashed_state }, trie }
}
/// Returns a reference to the trie updates for the block, if present.
#[inline]
pub fn trie_updates(&self) -> Option<&TrieUpdates> {
self.trie.as_ref()
}
/// Converts the value into [`SealedBlock`].
pub fn into_sealed_block(self) -> SealedBlock<N::Block> {
let block = Arc::unwrap_or_clone(self.block.recovered_block);
block.into_sealed_block()
}
}
/// Non-empty chain of blocks.
#[derive(Debug)]
pub enum NewCanonicalChain<N: NodePrimitives = EthPrimitives> {
/// A simple append to the current canonical head
Commit {
/// all blocks that lead back to the canonical head
new: Vec<ExecutedBlockWithTrieUpdates<N>>,
new: Vec<ExecutedBlock<N>>,
},
/// A reorged chain consists of two chains that trace back to a shared ancestor block at which
/// point they diverge.
Reorg {
/// All blocks of the _new_ chain
new: Vec<ExecutedBlockWithTrieUpdates<N>>,
new: Vec<ExecutedBlock<N>>,
/// All blocks of the _old_ chain
///
/// These are not [`ExecutedBlockWithTrieUpdates`] because we don't always have the trie
/// updates for the old canonical chain. For example, in case of node being restarted right
/// before the reorg [`TrieUpdates`] can't be fetched from database.
old: Vec<ExecutedBlock<N>>,
},
}
@@ -976,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(
@@ -1257,7 +1261,7 @@ mod tests {
block1.recovered_block().hash()
);
let chain = NewCanonicalChain::Reorg { new: vec![block2.clone()], old: vec![block1.block] };
let chain = NewCanonicalChain::Reorg { new: vec![block2.clone()], old: vec![block1] };
state.update_chain(chain);
assert_eq!(
state.head_state().unwrap().block_ref().recovered_block().hash(),
@@ -1380,8 +1384,7 @@ mod tests {
#[test]
fn test_canonical_in_memory_state_canonical_chain_empty() {
let state: CanonicalInMemoryState = CanonicalInMemoryState::empty();
let chain: Vec<_> = state.canonical_chain().collect();
assert!(chain.is_empty());
assert!(state.canonical_chain().next().is_none());
}
#[test]
@@ -1540,7 +1543,7 @@ mod tests {
// Test reorg notification
let chain_reorg = NewCanonicalChain::Reorg {
new: vec![block1a.clone(), block2a.clone()],
old: vec![block1.block.clone(), block2.block.clone()],
old: vec![block1.clone(), block2.clone()],
};
assert_eq!(

View File

@@ -6,11 +6,14 @@
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod in_memory;
pub use in_memory::*;
mod deferred_trie;
pub use deferred_trie::*;
mod noop;
mod chain_info;

View File

@@ -1,4 +1,4 @@
use super::ExecutedBlockWithTrieUpdates;
use super::ExecutedBlock;
use alloy_consensus::BlockHeader;
use alloy_primitives::{keccak256, Address, BlockNumber, Bytes, StorageKey, StorageValue, B256};
use reth_errors::ProviderResult;
@@ -24,7 +24,7 @@ pub struct MemoryOverlayStateProviderRef<
/// Historical state provider for state lookups that are not found in memory blocks.
pub(crate) historical: Box<dyn StateProvider + 'a>,
/// The collection of executed parent blocks. Expected order is newest to oldest.
pub(crate) in_memory: Vec<ExecutedBlockWithTrieUpdates<N>>,
pub(crate) in_memory: Vec<ExecutedBlock<N>>,
/// Lazy-loaded in-memory trie data.
pub(crate) trie_input: OnceLock<TrieInput>,
}
@@ -41,10 +41,7 @@ impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> {
/// - `in_memory` - the collection of executed ancestor blocks in reverse.
/// - `historical` - a historical state provider for the latest ancestor block stored in the
/// database.
pub fn new(
historical: Box<dyn StateProvider + 'a>,
in_memory: Vec<ExecutedBlockWithTrieUpdates<N>>,
) -> Self {
pub fn new(historical: Box<dyn StateProvider + 'a>, in_memory: Vec<ExecutedBlock<N>>) -> Self {
Self { historical, in_memory, trie_input: OnceLock::new() }
}
@@ -56,14 +53,20 @@ 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.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())),
)
})
}
fn merged_hashed_storage(&self, address: Address, storage: HashedStorage) -> HashedStorage {
let state = &self.trie_input().state;
let mut hashed = state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
hashed.extend(&storage);
hashed
}
}
impl<N: NodePrimitives> BlockHashReader for MemoryOverlayStateProviderRef<'_, N> {
@@ -84,12 +87,14 @@ impl<N: NodePrimitives> BlockHashReader for MemoryOverlayStateProviderRef<'_, N>
) -> ProviderResult<Vec<B256>> {
let range = start..end;
let mut earliest_block_number = None;
let mut in_memory_hashes = Vec::new();
let mut in_memory_hashes = Vec::with_capacity(range.size_hint().0);
// iterate in ascending order (oldest to newest = low to high)
for block in &self.in_memory {
if range.contains(&block.recovered_block().number()) {
let block_num = block.recovered_block().number();
if range.contains(&block_num) {
in_memory_hashes.push(block.recovered_block().hash());
earliest_block_number = Some(block.recovered_block().number());
earliest_block_number = Some(block_num);
}
}
@@ -146,11 +151,8 @@ impl<N: NodePrimitives> StateRootProvider for MemoryOverlayStateProviderRef<'_,
impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_, N> {
// TODO: Currently this does not reuse available in-memory trie nodes.
fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult<B256> {
let state = &self.trie_input().state;
let mut hashed_storage =
state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
hashed_storage.extend(&storage);
self.historical.storage_root(address, hashed_storage)
let merged = self.merged_hashed_storage(address, storage);
self.historical.storage_root(address, merged)
}
// TODO: Currently this does not reuse available in-memory trie nodes.
@@ -160,11 +162,8 @@ impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_
slot: B256,
storage: HashedStorage,
) -> ProviderResult<reth_trie::StorageProof> {
let state = &self.trie_input().state;
let mut hashed_storage =
state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
hashed_storage.extend(&storage);
self.historical.storage_proof(address, slot, hashed_storage)
let merged = self.merged_hashed_storage(address, storage);
self.historical.storage_proof(address, slot, merged)
}
// TODO: Currently this does not reuse available in-memory trie nodes.
@@ -174,11 +173,8 @@ impl<N: NodePrimitives> StorageRootProvider for MemoryOverlayStateProviderRef<'_
slots: &[B256],
storage: HashedStorage,
) -> ProviderResult<StorageMultiProof> {
let state = &self.trie_input().state;
let mut hashed_storage =
state.storages.get(&keccak256(address)).cloned().unwrap_or_default();
hashed_storage.extend(&storage);
self.historical.storage_multiproof(address, slots, hashed_storage)
let merged = self.merged_hashed_storage(address, storage);
self.historical.storage_multiproof(address, slots, merged)
}
}

View File

@@ -1,6 +1,6 @@
use crate::{
in_memory::ExecutedBlockWithTrieUpdates, CanonStateNotification, CanonStateNotifications,
CanonStateSubscriptions, ExecutedTrieUpdates,
in_memory::ExecutedBlock, CanonStateNotification, CanonStateNotifications,
CanonStateSubscriptions, ComputedTrieData,
};
use alloy_consensus::{Header, SignableTransaction, TxEip1559, TxReceipt, EMPTY_ROOT_HASH};
use alloy_eips::{
@@ -23,11 +23,10 @@ use reth_primitives_traits::{
SignedTransaction,
};
use reth_storage_api::NodePrimitivesProvider;
use reth_trie::{root::state_root_unhashed, HashedPostState};
use reth_trie::root::state_root_unhashed;
use revm_database::BundleState;
use revm_state::AccountInfo;
use std::{
collections::HashMap,
ops::Range,
sync::{Arc, Mutex},
};
@@ -93,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<_> {
@@ -146,12 +145,10 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
mix_hash: B256::random(),
gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
base_fee_per_gas: Some(INITIAL_BASE_FEE),
transactions_root: calculate_transaction_root(
&transactions.clone().into_iter().map(|tx| tx.into_inner()).collect::<Vec<_>>(),
),
transactions_root: calculate_transaction_root(&transactions),
receipts_root: calculate_receipt_root(&receipts),
beneficiary: Address::random(),
state_root: state_root_unhashed(HashMap::from([(
state_root: state_root_unhashed([(
self.signer,
Account {
balance: initial_signer_balance - signer_balance_decrease,
@@ -159,7 +156,7 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
..Default::default()
}
.into_trie_account(EMPTY_ROOT_HASH),
)])),
)]),
// use the number as the timestamp so it is monotonically increasing
timestamp: number +
EthereumHardfork::Cancun.activation_timestamp(self.chain_spec.chain).unwrap(),
@@ -170,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.
@@ -194,24 +188,26 @@ 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);
}
fork
}
/// Gets an [`ExecutedBlockWithTrieUpdates`] with [`BlockNumber`], receipts and parent hash.
/// Gets an [`ExecutedBlock`] with [`BlockNumber`], receipts and parent hash.
fn get_executed_block(
&mut self,
block_number: BlockNumber,
receipts: Vec<Vec<Receipt>>,
parent_hash: B256,
) -> ExecutedBlockWithTrieUpdates {
let block_with_senders = self.generate_random_block(block_number, parent_hash);
let (block, senders) = block_with_senders.split_sealed();
ExecutedBlockWithTrieUpdates::new(
) -> ExecutedBlock {
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(),
@@ -219,27 +215,26 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
block_number,
vec![Requests::default()],
)),
Arc::new(HashedPostState::default()),
ExecutedTrieUpdates::empty(),
trie_data,
)
}
/// Generates an [`ExecutedBlockWithTrieUpdates`] that includes the given receipts.
/// Generates an [`ExecutedBlock`] that includes the given receipts.
pub fn get_executed_block_with_receipts(
&mut self,
receipts: Vec<Vec<Receipt>>,
parent_hash: B256,
) -> ExecutedBlockWithTrieUpdates {
) -> ExecutedBlock {
let number = rand::rng().random::<u64>();
self.get_executed_block(number, receipts, parent_hash)
}
/// Generates an [`ExecutedBlockWithTrieUpdates`] with the given [`BlockNumber`].
/// Generates an [`ExecutedBlock`] with the given [`BlockNumber`].
pub fn get_executed_block_with_number(
&mut self,
block_number: BlockNumber,
parent_hash: B256,
) -> ExecutedBlockWithTrieUpdates {
) -> ExecutedBlock {
self.get_executed_block(block_number, vec![vec![]], parent_hash)
}
@@ -247,7 +242,7 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
pub fn get_executed_blocks(
&mut self,
range: Range<u64>,
) -> impl Iterator<Item = ExecutedBlockWithTrieUpdates> + '_ {
) -> impl Iterator<Item = ExecutedBlock> + '_ {
let mut parent_hash = B256::default();
range.map(move |number| {
let current_parent_hash = parent_hash;

View File

@@ -1,7 +1,6 @@
use crate::{ChainSpec, DepositContract};
use alloc::{boxed::Box, vec::Vec};
use alloy_chains::Chain;
use alloy_consensus::Header;
use alloy_eips::{calc_next_block_base_fee, eip1559::BaseFeeParams, eip7840::BlobParams};
use alloy_genesis::Genesis;
use alloy_primitives::{B256, U256};
@@ -75,8 +74,8 @@ pub trait EthChainSpec: Send + Sync + Unpin + Debug {
}
}
impl EthChainSpec for ChainSpec {
type Header = Header;
impl<H: BlockHeader> EthChainSpec for ChainSpec<H> {
type Header = H;
fn chain(&self) -> Chain {
self.chain

View File

@@ -6,7 +6,7 @@
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
@@ -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;

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,11 @@ pub trait ChainSpecParser: Clone + Send + Sync + 'static {
/// List of supported chains.
const SUPPORTED_CHAINS: &'static [&'static str];
/// The default value for the chain spec parser.
fn default_value() -> Option<&'static str> {
Self::SUPPORTED_CHAINS.first().copied()
}
/// Parses the given string into a chain spec.
///
/// # Arguments

View File

@@ -6,7 +6,7 @@
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
use clap::{Error, Parser};
use reth_cli_runner::CliRunner;

View File

@@ -21,7 +21,7 @@ reth-consensus.workspace = true
reth-db = { workspace = true, features = ["mdbx"] }
reth-db-api.workspace = true
reth-db-common.workspace = true
reth-downloaders.workspace = true
reth-downloaders = { workspace = true, features = ["file-client"] }
reth-ecies.workspace = true
reth-eth-wire.workspace = true
reth-era.workspace = true
@@ -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
@@ -99,6 +101,8 @@ proptest-arbitrary-interop = { workspace = true, optional = true }
[dev-dependencies]
reth-ethereum-cli.workspace = true
reth-provider = { workspace = true, features = ["test-utils"] }
tempfile.workspace = true
[features]
default = []

View File

@@ -1,13 +1,15 @@
//! Contains common `reth` arguments
pub use reth_primitives_traits::header::HeaderMut;
use alloy_primitives::B256;
use clap::Parser;
use reth_chainspec::EthChainSpec;
use reth_cli::chainspec::ChainSpecParser;
use reth_config::{config::EtlConfig, Config};
use reth_consensus::{noop::NoopConsensus, ConsensusError, FullConsensus};
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,11 @@ 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},
ProviderFactory, StaticFileProviderFactory,
};
use reth_stages::{sets::DefaultStages, Pipeline, PipelineTarget};
@@ -48,7 +50,7 @@ pub struct EnvironmentArgs<C: ChainSpecParser> {
long,
value_name = "CHAIN_OR_PATH",
long_help = C::help_message(),
default_value = C::SUPPORTED_CHAINS[0],
default_value = C::default_value(),
value_parser = C::parser(),
global = true
)]
@@ -57,6 +59,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 +75,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());
@@ -97,16 +105,22 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
Arc::new(init_db(db_path, self.db.database_args())?),
StaticFileProvider::read_write(sf_path)?,
),
AccessRights::RO => (
AccessRights::RO | AccessRights::RoInconsistent => (
Arc::new(open_db_read_only(&db_path, self.db.database_args())?),
StaticFileProvider::read_only(sf_path, false)?,
),
};
// 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,24 +136,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.as_ref().is_some_and(|a| a.has_receipts_pruning());
let prune_modes =
config.prune.as_ref().map(|prune| prune.segments.clone()).unwrap_or_default();
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.");
@@ -200,6 +215,8 @@ pub enum AccessRights {
RW,
/// Read-only access
RO,
/// Read-only access with possibly inconsistent data
RoInconsistent,
}
impl AccessRights {
@@ -207,6 +224,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.
@@ -216,20 +239,9 @@ 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: NodeTypesForProvider {
pub trait CliNodeTypes: Node<FullTypesAdapter<Self>> + NodeTypesForProvider {
type Evm: ConfigureEvm<Primitives = Self::Primitives>;
type NetworkPrimitives: NetPrimitivesFor<Self::Primitives>;
}
@@ -242,32 +254,29 @@ where
type NetworkPrimitives = <<<N::ComponentsBuilder as NodeComponentsBuilder<FullTypesAdapter<Self>>>::Components as NodeComponents<FullTypesAdapter<Self>>>::Network as NetworkEventListenerProvider>::Primitives;
}
type EvmFor<N> = <<<N as Node<FullTypesAdapter<N>>>::ComponentsBuilder as NodeComponentsBuilder<
FullTypesAdapter<N>,
>>::Components as NodeComponents<FullTypesAdapter<N>>>::Evm;
type ConsensusFor<N> =
<<<N as Node<FullTypesAdapter<N>>>::ComponentsBuilder as NodeComponentsBuilder<
FullTypesAdapter<N>,
>>::Components as NodeComponents<FullTypesAdapter<N>>>::Consensus;
/// Helper trait aggregating components required for the CLI.
pub trait CliNodeComponents<N: CliNodeTypes>: Send + Sync + 'static {
/// Evm to use.
type Evm: ConfigureEvm<Primitives = N::Primitives> + 'static;
/// Consensus implementation.
type Consensus: FullConsensus<N::Primitives, Error = ConsensusError> + Clone + 'static;
/// Returns the configured EVM.
fn evm_config(&self) -> &Self::Evm;
fn evm_config(&self) -> &EvmFor<N>;
/// Returns the consensus implementation.
fn consensus(&self) -> &Self::Consensus;
fn consensus(&self) -> &ConsensusFor<N>;
}
impl<N: CliNodeTypes, E, C> CliNodeComponents<N> for (E, C)
where
E: ConfigureEvm<Primitives = N::Primitives> + 'static,
C: FullConsensus<N::Primitives, Error = ConsensusError> + Clone + 'static,
{
type Evm = E;
type Consensus = C;
fn evm_config(&self) -> &Self::Evm {
impl<N: CliNodeTypes> CliNodeComponents<N> for (EvmFor<N>, ConsensusFor<N>) {
fn evm_config(&self) -> &EvmFor<N> {
&self.0
}
fn consensus(&self) -> &Self::Consensus {
fn consensus(&self) -> &ConsensusFor<N> {
&self.1
}
}

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

@@ -1,19 +1,24 @@
use alloy_consensus::Header;
use alloy_primitives::{hex, BlockHash};
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::{ReceiptTy, TxTy};
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;
@@ -39,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,
@@ -61,33 +74,36 @@ 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 {
StaticFileSegment::Headers => {
(table_key::<tables::Headers>(&key)?, <HeaderWithHashMask<Header>>::MASK)
}
StaticFileSegment::Headers => (
table_key::<tables::Headers>(&key)?,
<HeaderWithHashMask<HeaderTy<N>>>::MASK,
),
StaticFileSegment::Transactions => {
(table_key::<tables::Transactions>(&key)?, <TransactionMask<TxTy<N>>>::MASK)
}
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) => {
@@ -96,7 +112,7 @@ impl Command {
} else {
match segment {
StaticFileSegment::Headers => {
let header = Header::decompress(content[0].as_slice())?;
let header = HeaderTy::<N>::decompress(content[0].as_slice())?;
let block_hash = BlockHash::decompress(content[1].as_slice())?;
println!(
"Header\n{}\n\nBlockHash\n{}",
@@ -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,8 +96,11 @@ 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: {}", stringify!($table)))?;
let stats = tx.inner.db_stat(&table_db).wrap_err(format!("Could not find table: {}", self.args.table.name()))?;
let total_entries = stats.entries();
let final_entry_idx = total_entries.saturating_sub(1);
if self.args.skip > final_entry_idx {

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,25 +55,35 @@ 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.clone())?;
let $tool = DbTool::new(provider_factory)?;
$command;
};
}
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,17 +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 Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
command.execute(provider_factory)?;
let access_rights =
if command.dry_run { AccessRights::RO } else { AccessRights::RW };
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) {
@@ -160,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,130 +1,313 @@
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::ProviderFactory;
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);
/// The arguments for the `reth db repair-trie` command
#[derive(Parser, Debug)]
pub struct Command {
/// Only show inconsistencies without making any repairs
#[arg(long)]
dry_run: bool,
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: NodeTypesWithDB>(
pub fn execute<N: ProviderNodeTypes>(
self,
provider_factory: ProviderFactory<N>,
tool: &DbTool<N>,
task_executor: TaskExecutor,
) -> eyre::Result<()> {
// Get a database transaction directly from the database
let db = provider_factory.db_ref();
let mut tx = db.tx_mut()?;
tx.disable_long_read_transaction_safety();
// 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();
// Create the hashed cursor factory
let hashed_cursor_factory = DatabaseHashedCursorFactory::new(&tx);
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(),
);
// Create the trie cursor factory
let trie_cursor_factory = DatabaseTrieCursorFactory::new(&tx);
// Create the verifier
let verifier = Verifier::new(trie_cursor_factory, hashed_cursor_factory)?;
let mut account_trie_cursor = tx.cursor_write::<tables::AccountsTrie>()?;
let mut storage_trie_cursor = tx.cursor_dup_write::<tables::StoragesTrie>()?;
let mut inconsistent_nodes = 0;
let start_time = Instant::now();
let mut last_progress_time = Instant::now();
// Iterate over the verifier and repair inconsistencies
for output_result in verifier {
let output = output_result?;
if let Output::Progress(path) = output {
// Output progress every 5 seconds
if last_progress_time.elapsed() > Duration::from_secs(5) {
output_progress(path, start_time, inconsistent_nodes);
last_progress_time = Instant::now();
// Spawn the metrics server
if let Err(e) = MetricServer::new(config).serve().await {
tracing::error!("Metrics server error: {}", e);
}
continue
};
});
warn!("Inconsistency found, will repair: {output:?}");
inconsistent_nodes += 1;
if self.dry_run {
continue;
}
match output {
Output::AccountExtra(path, _node) => {
// Extra account node in trie, remove it
let nibbles = StoredNibbles(path);
if account_trie_cursor.seek_exact(nibbles)?.is_some() {
account_trie_cursor.delete_current()?;
}
}
Output::StorageExtra(account, path, _node) => {
// Extra storage node in trie, remove it
let nibbles = StoredNibblesSubKey(path);
if storage_trie_cursor
.seek_by_key_subkey(account, nibbles.clone())?
.filter(|e| e.nibbles == nibbles)
.is_some()
{
storage_trie_cursor.delete_current()?;
}
}
Output::AccountWrong { path, expected: node, .. } |
Output::AccountMissing(path, node) => {
// Wrong/missing account node value, upsert it
let nibbles = StoredNibbles(path);
account_trie_cursor.upsert(nibbles, &node)?;
}
Output::StorageWrong { account, path, expected: node, .. } |
Output::StorageMissing(account, path, node) => {
// Wrong/missing storage node value, upsert it
let nibbles = StoredNibblesSubKey(path);
let entry = StorageTrieEntry { nibbles, node };
storage_trie_cursor.upsert(account, &entry)?;
}
Output::Progress(_) => {
unreachable!()
}
}
}
if inconsistent_nodes > 0 {
if self.dry_run {
info!("Found {} inconsistencies (dry run - no changes made)", inconsistent_nodes);
} else {
info!("Repaired {} inconsistencies", inconsistent_nodes);
tx.commit()?;
info!("Changes committed to database");
}
Some(handle)
} else {
info!("No inconsistencies found");
None
};
if self.dry_run {
verify_only(tool)?
} else {
verify_and_repair(tool)?
}
Ok(())
}
}
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 = tool.provider_factory.db_ref();
let mut tx = db.tx()?;
tx.disable_long_read_transaction_safety();
// Create the verifier
let hashed_cursor_factory = DatabaseHashedCursorFactory::new(&tx);
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();
// Iterate over the verifier and repair inconsistencies
for output_result in verifier {
let output = output_result?;
if let Output::Progress(path) = output {
if last_progress_time.elapsed() > PROGRESS_PERIOD {
output_progress(path, start_time, inconsistent_nodes);
last_progress_time = Instant::now();
}
} 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!(),
}
}
}
info!("Found {} inconsistencies (dry run - no changes made)", inconsistent_nodes);
Ok(())
}
/// Checks that the merkle stage has completed running up to the account and storage hashing stages.
fn verify_checkpoints(provider: impl StageCheckpointReader) -> eyre::Result<()> {
let account_hashing_checkpoint =
provider.get_stage_checkpoint(StageId::AccountHashing)?.unwrap_or_default();
let storage_hashing_checkpoint =
provider.get_stage_checkpoint(StageId::StorageHashing)?.unwrap_or_default();
let merkle_checkpoint =
provider.get_stage_checkpoint(StageId::MerkleExecute)?.unwrap_or_default();
if account_hashing_checkpoint.block_number != merkle_checkpoint.block_number {
return Err(eyre::eyre!(
"MerkleExecute stage checkpoint ({}) != AccountHashing stage checkpoint ({}), you must first complete the pipeline sync by running `reth node`",
merkle_checkpoint.block_number,
account_hashing_checkpoint.block_number,
))
}
if storage_hashing_checkpoint.block_number != merkle_checkpoint.block_number {
return Err(eyre::eyre!(
"MerkleExecute stage checkpoint ({}) != StorageHashing stage checkpoint ({}), you must first complete the pipeline sync by running `reth node`",
merkle_checkpoint.block_number,
storage_hashing_checkpoint.block_number,
))
}
let merkle_checkpoint_progress =
provider.get_stage_checkpoint_progress(StageId::MerkleExecute)?;
if merkle_checkpoint_progress.is_some_and(|progress| !progress.is_empty()) {
return Err(eyre::eyre!(
"MerkleExecute sync stage in-progress, you must first complete the pipeline sync by running `reth node`",
))
}
Ok(())
}
fn verify_and_repair<N: ProviderNodeTypes>(tool: &DbTool<N>) -> eyre::Result<()> {
// Get a read-write database provider
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())?;
// Create cursors for making modifications with
let tx = provider_rw.tx_mut();
tx.disable_long_read_transaction_safety();
let mut account_trie_cursor = tx.cursor_write::<tables::AccountsTrie>()?;
let mut storage_trie_cursor = tx.cursor_dup_write::<tables::StoragesTrie>()?;
// Create the cursor factories. These cannot accept the `&mut` tx above because they require it
// to be AsRef.
let tx = provider_rw.tx_ref();
let hashed_cursor_factory = DatabaseHashedCursorFactory::new(tx);
let trie_cursor_factory = DatabaseTrieCursorFactory::new(tx);
// 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();
// Iterate over the verifier and repair inconsistencies
for output_result in verifier {
let output = output_result?;
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 {
Output::AccountExtra(path, _node) => {
// Extra account node in trie, remove it
let nibbles = StoredNibbles(path);
if account_trie_cursor.seek_exact(nibbles)?.is_some() {
account_trie_cursor.delete_current()?;
}
}
Output::StorageExtra(account, path, _node) => {
// Extra storage node in trie, remove it
let nibbles = StoredNibblesSubKey(path);
if storage_trie_cursor
.seek_by_key_subkey(account, nibbles.clone())?
.filter(|e| e.nibbles == nibbles)
.is_some()
{
storage_trie_cursor.delete_current()?;
}
}
Output::AccountWrong { path, expected: node, .. } |
Output::AccountMissing(path, node) => {
// Wrong/missing account node value, upsert it
let nibbles = StoredNibbles(path);
account_trie_cursor.upsert(nibbles, &node)?;
}
Output::StorageWrong { account, path, expected: node, .. } |
Output::StorageMissing(account, path, node) => {
// Wrong/missing storage node value, upsert it
// (We can't just use `upsert` method with a dup cursor, it's not properly
// supported)
let nibbles = StoredNibblesSubKey(path);
let entry = StorageTrieEntry { nibbles: nibbles.clone(), node };
if storage_trie_cursor
.seek_by_key_subkey(account, nibbles.clone())?
.filter(|v| v.nibbles == nibbles)
.is_some()
{
storage_trie_cursor.delete_current()?;
}
storage_trie_cursor.upsert(account, &entry)?;
}
Output::Progress(path) => {
if last_progress_time.elapsed() > PROGRESS_PERIOD {
output_progress(path, start_time, inconsistent_nodes);
last_progress_time = Instant::now();
}
}
}
}
if inconsistent_nodes == 0 {
info!("No inconsistencies found");
} else {
info!("Repaired {} inconsistencies, committing changes", inconsistent_nodes);
provider_rw.commit()?;
}
Ok(())
}
/// Output progress information based on the last seen account path.
fn output_progress(last_account: Nibbles, start_time: Instant, inconsistent_nodes: u64) {
// Calculate percentage based on position in the trie path space
@@ -161,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

@@ -7,9 +7,10 @@ use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_fs_util as fs;
use std::{
borrow::Cow,
io::{self, Read, Write},
path::Path,
sync::Arc,
sync::{Arc, OnceLock},
time::{Duration, Instant},
};
use tar::Archive;
@@ -22,24 +23,109 @@ const MERKLE_BASE_URL: &str = "https://downloads.merkle.io";
const EXTENSION_TAR_LZ4: &str = ".tar.lz4";
const EXTENSION_TAR_ZSTD: &str = ".tar.zst";
/// Global static download defaults
static DOWNLOAD_DEFAULTS: OnceLock<DownloadDefaults> = OnceLock::new();
/// Download configuration defaults
///
/// Global defaults can be set via [`DownloadDefaults::try_init`].
#[derive(Debug, Clone)]
pub struct DownloadDefaults {
/// List of available snapshot sources
pub available_snapshots: Vec<Cow<'static, str>>,
/// Default base URL for snapshots
pub default_base_url: Cow<'static, str>,
/// Optional custom long help text that overrides the generated help
pub long_help: Option<String>,
}
impl DownloadDefaults {
/// Initialize the global download defaults with this configuration
pub fn try_init(self) -> Result<(), Self> {
DOWNLOAD_DEFAULTS.set(self)
}
/// Get a reference to the global download defaults
pub fn get_global() -> &'static DownloadDefaults {
DOWNLOAD_DEFAULTS.get_or_init(DownloadDefaults::default_download_defaults)
}
/// Default download configuration with defaults from merkle.io and publicnode
pub fn default_download_defaults() -> Self {
Self {
available_snapshots: vec![
Cow::Borrowed("https://www.merkle.io/snapshots (default, mainnet archive)"),
Cow::Borrowed("https://publicnode.com/snapshots (full nodes & testnets)"),
],
default_base_url: Cow::Borrowed(MERKLE_BASE_URL),
long_help: None,
}
}
/// Generates the long help text for the download URL argument using these defaults.
///
/// If a custom long_help is set, it will be returned. Otherwise, help text is generated
/// from the available_snapshots list.
pub fn long_help(&self) -> String {
if let Some(ref custom_help) = self.long_help {
return custom_help.clone();
}
let mut help = String::from(
"Specify a snapshot URL or let the command propose a default one.\n\nAvailable snapshot sources:\n",
);
for source in &self.available_snapshots {
help.push_str("- ");
help.push_str(source);
help.push('\n');
}
help.push_str(
"\nIf no URL is provided, the latest mainnet archive snapshot\nwill be proposed for download from ",
);
help.push_str(self.default_base_url.as_ref());
help
}
/// Add a snapshot source to the list
pub fn with_snapshot(mut self, source: impl Into<Cow<'static, str>>) -> Self {
self.available_snapshots.push(source.into());
self
}
/// Replace all snapshot sources
pub fn with_snapshots(mut self, sources: Vec<Cow<'static, str>>) -> Self {
self.available_snapshots = sources;
self
}
/// Set the default base URL, e.g. `https://downloads.merkle.io`.
pub fn with_base_url(mut self, url: impl Into<Cow<'static, str>>) -> Self {
self.default_base_url = url.into();
self
}
/// Builder: Set custom long help text, overriding the generated help
pub fn with_long_help(mut self, help: impl Into<String>) -> Self {
self.long_help = Some(help.into());
self
}
}
impl Default for DownloadDefaults {
fn default() -> Self {
Self::default_download_defaults()
}
}
#[derive(Debug, Parser)]
pub struct DownloadCommand<C: ChainSpecParser> {
#[command(flatten)]
env: EnvironmentArgs<C>,
#[arg(
long,
short,
help = "Custom URL to download the snapshot from",
long_help = "Specify a snapshot URL or let the command propose a default one.\n\
\n\
Available snapshot sources:\n\
- https://www.merkle.io/snapshots (default, mainnet archive)\n\
- https://publicnode.com/snapshots (full nodes & testnets)\n\
\n\
If no URL is provided, the latest mainnet archive snapshot\n\
will be proposed for download from merkle.io"
)]
/// Custom URL to download the snapshot from
#[arg(long, short, long_help = DownloadDefaults::get_global().long_help())]
url: Option<String>,
}
@@ -141,10 +227,10 @@ impl<R: Read> ProgressReader<R> {
impl<R: Read> Read for ProgressReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let bytes = self.reader.read(buf)?;
if bytes > 0 {
if let Err(e) = self.progress.update(bytes as u64) {
return Err(io::Error::other(e));
}
if bytes > 0 &&
let Err(e) = self.progress.update(bytes as u64)
{
return Err(io::Error::other(e));
}
Ok(bytes)
}
@@ -207,9 +293,10 @@ async fn stream_and_extract(url: &str, target_dir: &Path) -> Result<()> {
Ok(())
}
// Builds default URL for latest mainnet archive snapshot
// Builds default URL for latest mainnet archive snapshot using configured defaults
async fn get_latest_snapshot_url() -> Result<String> {
let latest_url = format!("{MERKLE_BASE_URL}/latest.txt");
let base_url = &DownloadDefaults::get_global().default_base_url;
let latest_url = format!("{base_url}/latest.txt");
let filename = Client::new()
.get(latest_url)
.send()
@@ -220,5 +307,64 @@ async fn get_latest_snapshot_url() -> Result<String> {
.trim()
.to_string();
Ok(format!("{MERKLE_BASE_URL}/{filename}"))
Ok(format!("{base_url}/{filename}"))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_download_defaults_builder() {
let defaults = DownloadDefaults::default()
.with_snapshot("https://example.com/snapshots (example)")
.with_base_url("https://example.com");
assert_eq!(defaults.default_base_url, "https://example.com");
assert_eq!(defaults.available_snapshots.len(), 3); // 2 defaults + 1 added
}
#[test]
fn test_download_defaults_replace_snapshots() {
let defaults = DownloadDefaults::default().with_snapshots(vec![
Cow::Borrowed("https://custom1.com"),
Cow::Borrowed("https://custom2.com"),
]);
assert_eq!(defaults.available_snapshots.len(), 2);
assert_eq!(defaults.available_snapshots[0], "https://custom1.com");
}
#[test]
fn test_long_help_generation() {
let defaults = DownloadDefaults::default();
let help = defaults.long_help();
assert!(help.contains("Available snapshot sources:"));
assert!(help.contains("merkle.io"));
assert!(help.contains("publicnode.com"));
}
#[test]
fn test_long_help_override() {
let custom_help = "This is custom help text for downloading snapshots.";
let defaults = DownloadDefaults::default().with_long_help(custom_help);
let help = defaults.long_help();
assert_eq!(help, custom_help);
assert!(!help.contains("Available snapshot sources:"));
}
#[test]
fn test_builder_chaining() {
let defaults = DownloadDefaults::default()
.with_base_url("https://custom.example.com")
.with_snapshot("https://snapshot1.com")
.with_snapshot("https://snapshot2.com")
.with_long_help("Custom help for snapshots");
assert_eq!(defaults.default_base_url, "https://custom.example.com");
assert_eq!(defaults.available_snapshots.len(), 4); // 2 defaults + 2 added
assert_eq!(defaults.long_help, Some("Custom help for snapshots".to_string()));
}
}

View File

@@ -15,7 +15,7 @@ pub struct DumpGenesisCommand<C: ChainSpecParser> {
long,
value_name = "CHAIN_OR_PATH",
long_help = C::help_message(),
default_value = C::SUPPORTED_CHAINS[0],
default_value = C::default_value(),
value_parser = C::parser()
)]
chain: Arc<C::ChainSpec>,

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

@@ -102,6 +102,9 @@ where
.sealed_header(provider_factory.last_block_number()?)?
.expect("should have genesis");
let static_file_producer =
StaticFileProducer::new(provider_factory.clone(), PruneModes::default());
while let Some(file_client) =
reader.next_chunk::<BlockTy<N>>(consensus.clone(), Some(sealed_header)).await?
{
@@ -121,7 +124,7 @@ where
provider_factory.clone(),
&consensus,
Arc::new(file_client),
StaticFileProducer::new(provider_factory.clone(), PruneModes::default()),
static_file_producer.clone(),
import_config.no_state,
executor.clone(),
)?;
@@ -192,7 +195,7 @@ pub fn build_import_pipeline_impl<N, C, E>(
static_file_producer: StaticFileProducer<ProviderFactory<N>>,
disable_exec: bool,
evm_config: E,
) -> eyre::Result<(Pipeline<N>, impl futures::Stream<Item = NodeEvent<N::Primitives>>)>
) -> eyre::Result<(Pipeline<N>, impl futures::Stream<Item = NodeEvent<N::Primitives>> + use<N, C, E>)>
where
N: ProviderNodeTypes,
C: FullConsensus<N::Primitives, Error = reth_consensus::ConsensusError> + 'static,

View File

@@ -1,18 +1,19 @@
//! 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::{B256, U256};
use alloy_primitives::{Sealable, B256};
use clap::Parser;
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, DatabaseProviderFactory, StaticFileProviderFactory, StaticFileWriter,
BlockNumReader, DBProvider, DatabaseProviderFactory, StaticFileProviderFactory,
StaticFileWriter,
};
use std::{io::BufReader, path::PathBuf, str::FromStr, sync::Arc};
use std::{io::BufReader, path::PathBuf, sync::Arc};
use tracing::info;
pub mod without_evm;
@@ -57,13 +58,9 @@ pub struct InitStateCommand<C: ChainSpecParser> {
#[arg(long, value_name = "HEADER_FILE", verbatim_doc_comment)]
pub header: Option<PathBuf>,
/// Total difficulty of the header.
#[arg(long, value_name = "TOTAL_DIFFICULTY", verbatim_doc_comment)]
pub total_difficulty: Option<String>,
/// Hash of the header.
#[arg(long, value_name = "HEADER_HASH", verbatim_doc_comment)]
pub header_hash: Option<String>,
pub header_hash: Option<B256>,
}
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateCommand<C> {
@@ -72,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");
@@ -87,16 +84,9 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
let header = self.header.ok_or_else(|| eyre::eyre!("Header file must be provided"))?;
let header = without_evm::read_header_from_file::<
<N::Primitives as NodePrimitives>::BlockHeader,
>(header)?;
>(&header)?;
let header_hash =
self.header_hash.ok_or_else(|| eyre::eyre!("Header hash must be provided"))?;
let header_hash = B256::from_str(&header_hash)?;
let total_difficulty = self
.total_difficulty
.ok_or_else(|| eyre::eyre!("Total difficulty must be provided"))?;
let total_difficulty = U256::from_str(&total_difficulty)?;
let header_hash = self.header_hash.unwrap_or_else(|| header.hash_slow());
let last_block_number = provider_rw.last_block_number()?;
@@ -104,7 +94,6 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
without_evm::setup_without_evm(
&provider_rw,
SealedHeader::new(header, header_hash),
total_difficulty,
|number| {
let mut header =
<<N::Primitives as NodePrimitives>::BlockHeader>::default();
@@ -121,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."
));
}
}
@@ -145,3 +134,32 @@ impl<C: ChainSpecParser> InitStateCommand<C> {
Some(&self.env.chain)
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::b256;
use reth_ethereum_cli::chainspec::EthereumChainSpecParser;
#[test]
fn parse_init_state_command_with_without_evm() {
let cmd: InitStateCommand<EthereumChainSpecParser> = InitStateCommand::parse_from([
"reth",
"--chain",
"sepolia",
"--without-evm",
"--header",
"header.rlp",
"--header-hash",
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"state.jsonl",
]);
assert_eq!(cmd.state.to_str().unwrap(), "state.jsonl");
assert!(cmd.without_evm);
assert_eq!(cmd.header.unwrap().to_str().unwrap(), "header.rlp");
assert_eq!(
cmd.header_hash.unwrap(),
b256!("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef")
);
}
}

View File

@@ -1,25 +1,31 @@
use alloy_consensus::BlockHeader;
use alloy_primitives::{BlockNumber, B256, U256};
use alloy_primitives::{BlockNumber, B256};
use alloy_rlp::Decodable;
use reth_codecs::Compact;
use reth_node_builder::NodePrimitives;
use reth_primitives_traits::{SealedBlock, SealedHeader, SealedHeaderFor};
use reth_provider::{
providers::StaticFileProvider, BlockWriter, ProviderResult, StageCheckpointWriter,
StaticFileProviderFactory, StaticFileWriter, StorageLocation,
StaticFileProviderFactory, StaticFileWriter,
};
use reth_stages::{StageCheckpoint, StageId};
use reth_static_file_types::StaticFileSegment;
use std::{fs::File, io::Read, path::PathBuf};
use std::path::Path;
use tracing::info;
/// Reads the header RLP from a file and returns the Header.
pub(crate) fn read_header_from_file<H>(path: PathBuf) -> Result<H, eyre::Error>
///
/// This supports both raw rlp bytes and rlp hex string.
pub(crate) fn read_header_from_file<H>(path: &Path) -> Result<H, eyre::Error>
where
H: Decodable,
{
let mut file = File::open(path)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
let buf = if let Ok(content) = reth_fs_util::read_to_string(path) {
alloy_primitives::hex::decode(content.trim())?
} else {
// If UTF-8 decoding fails, read as raw bytes
reth_fs_util::read(path)?
};
let header = H::decode(&mut &buf[..])?;
Ok(header)
@@ -30,7 +36,6 @@ where
pub fn setup_without_evm<Provider, F>(
provider_rw: &Provider,
header: SealedHeader<<Provider::Primitives as NodePrimitives>::BlockHeader>,
total_difficulty: U256,
header_factory: F,
) -> ProviderResult<()>
where
@@ -50,7 +55,7 @@ where
info!(target: "reth::cli", "Appending first valid block.");
append_first_block(provider_rw, &header, total_difficulty)?;
append_first_block(provider_rw, &header)?;
for stage in StageId::ALL {
provider_rw.save_stage_checkpoint(stage, StageCheckpoint::new(header.number()))?;
@@ -68,7 +73,6 @@ where
fn append_first_block<Provider>(
provider_rw: &Provider,
header: &SealedHeaderFor<Provider::Primitives>,
total_difficulty: U256,
) -> ProviderResult<()>
where
Provider: BlockWriter<Block = <Provider::Primitives as NodePrimitives>::Block>
@@ -81,21 +85,12 @@ where
)
.try_recover()
.expect("no senders or txes"),
StorageLocation::Database,
)?;
let sf_provider = provider_rw.static_file_provider();
sf_provider.latest_writer(StaticFileSegment::Headers)?.append_header(
header,
total_difficulty,
&header.hash(),
)?;
sf_provider.latest_writer(StaticFileSegment::Receipts)?.increment_block(header.number())?;
sf_provider.latest_writer(StaticFileSegment::Transactions)?.increment_block(header.number())?;
Ok(())
}
@@ -138,7 +133,7 @@ where
for block_num in 1..=target_height {
// TODO: should we fill with real parent_hash?
let header = header_factory(block_num);
writer.append_header(&header, U256::ZERO, &B256::ZERO)?;
writer.append_header(&header, &B256::ZERO)?;
}
Ok(())
});
@@ -168,3 +163,85 @@ where
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_consensus::Header;
use alloy_primitives::{address, b256};
use reth_db_common::init::init_genesis;
use reth_provider::{test_utils::create_test_provider_factory, DatabaseProviderFactory};
use std::io::Write;
use tempfile::NamedTempFile;
#[test]
fn test_read_header_from_file_hex_string() {
let header_rlp = "0xf90212a00d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479471562b71999873db5b286df957af199ec94617f7a0f496f3d199c51a1aaee67dac95f24d92ac13c60d25181e1eecd6eca5ddf32ac0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808206a4840365908a808468e975f09ad983011003846765746888676f312e32352e308664617277696ea06f485a167165ec12e0ab3e6ab59a7b88560b90306ac98a26eb294abf95a8c59b88000000000000000007";
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(header_rlp.as_bytes()).unwrap();
temp_file.flush().unwrap();
let header: Header = read_header_from_file(temp_file.path()).unwrap();
assert_eq!(header.number, 1700);
assert_eq!(
header.parent_hash,
b256!("0d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dd")
);
assert_eq!(header.beneficiary, address!("71562b71999873db5b286df957af199ec94617f7"));
}
#[test]
fn test_read_header_from_file_raw_bytes() {
let header_rlp = "0xf90212a00d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479471562b71999873db5b286df957af199ec94617f7a0f496f3d199c51a1aaee67dac95f24d92ac13c60d25181e1eecd6eca5ddf32ac0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808206a4840365908a808468e975f09ad983011003846765746888676f312e32352e308664617277696ea06f485a167165ec12e0ab3e6ab59a7b88560b90306ac98a26eb294abf95a8c59b88000000000000000007";
let header_bytes =
alloy_primitives::hex::decode(header_rlp.trim_start_matches("0x")).unwrap();
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(&header_bytes).unwrap();
temp_file.flush().unwrap();
let header: Header = read_header_from_file(temp_file.path()).unwrap();
assert_eq!(header.number, 1700);
assert_eq!(
header.parent_hash,
b256!("0d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dd")
);
assert_eq!(header.beneficiary, address!("71562b71999873db5b286df957af199ec94617f7"));
}
#[test]
fn test_setup_without_evm_succeeds() {
let header_rlp = "0xf90212a00d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479471562b71999873db5b286df957af199ec94617f7a0f496f3d199c51a1aaee67dac95f24d92ac13c60d25181e1eecd6eca5ddf32ac0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808206a4840365908a808468e975f09ad983011003846765746888676f312e32352e308664617277696ea06f485a167165ec12e0ab3e6ab59a7b88560b90306ac98a26eb294abf95a8c59b88000000000000000007";
let header_bytes =
alloy_primitives::hex::decode(header_rlp.trim_start_matches("0x")).unwrap();
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(&header_bytes).unwrap();
temp_file.flush().unwrap();
let header: Header = read_header_from_file(temp_file.path()).unwrap();
let header_hash = b256!("4f05e4392969fc82e41f6d6a8cea379323b0b2d3ddf7def1a33eec03883e3a33");
let provider_factory = create_test_provider_factory();
init_genesis(&provider_factory).unwrap();
let provider_rw = provider_factory.database_provider_rw().unwrap();
setup_without_evm(&provider_rw, SealedHeader::new(header, header_hash), |number| Header {
number,
..Default::default()
})
.unwrap();
let static_files = provider_factory.static_file_provider();
let writer = static_files.latest_writer(StaticFileSegment::Headers).unwrap();
let actual_next_height = writer.next_block_number();
let expected_next_height = 1701;
assert_eq!(actual_next_height, expected_next_height);
}
}

View File

@@ -66,6 +66,12 @@ impl<F> FnLauncher<F> {
}
}
impl<F> fmt::Debug for FnLauncher<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FnLauncher").field("func", &"<function>").finish()
}
}
impl<C, Ext, F> Launcher<C, Ext> for FnLauncher<F>
where
C: ChainSpecParser,

View File

@@ -6,7 +6,7 @@
issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/"
)]
#![cfg_attr(not(test), warn(unused_crate_dependencies))]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(docsrs, feature(doc_cfg))]
pub mod common;
pub mod config_cmd;
@@ -24,7 +24,6 @@ pub mod node;
pub mod p2p;
pub mod prune;
pub mod re_execute;
pub mod recover;
pub mod stage;
#[cfg(feature = "arbitrary")]
pub mod test_vectors;

View File

@@ -5,18 +5,17 @@ use clap::{value_parser, Args, Parser};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_runner::CliContext;
use reth_cli_util::parse_socket_address;
use reth_db::init_db;
use reth_node_builder::NodeBuilder;
use reth_node_core::{
args::{
DatabaseArgs, DatadirArgs, DebugArgs, DevArgs, EngineArgs, EraArgs, NetworkArgs,
PayloadBuilderArgs, PruningArgs, RpcServerArgs, TxPoolArgs,
DatabaseArgs, DatadirArgs, DebugArgs, DevArgs, EngineArgs, EraArgs, MetricArgs,
NetworkArgs, PayloadBuilderArgs, PruningArgs, RpcServerArgs, StaticFilesArgs, TxPoolArgs,
},
node_config::NodeConfig,
version,
};
use std::{ffi::OsString, fmt, net::SocketAddr, path::PathBuf, sync::Arc};
use std::{ffi::OsString, fmt, path::PathBuf, sync::Arc};
/// Start the node
#[derive(Debug, Parser)]
@@ -32,18 +31,16 @@ pub struct NodeCommand<C: ChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs
long,
value_name = "CHAIN_OR_PATH",
long_help = C::help_message(),
default_value = C::SUPPORTED_CHAINS[0],
default_value = C::default_value(),
default_value_if("dev", "true", "dev"),
value_parser = C::parser(),
required = false,
)]
pub chain: Arc<C::ChainSpec>,
/// Enable Prometheus metrics.
///
/// The metrics will be served at the given interface and port.
#[arg(long, value_name = "SOCKET", value_parser = parse_socket_address, help_heading = "Metrics")]
pub metrics: Option<SocketAddr>,
/// Prometheus metrics configuration.
#[command(flatten)]
pub metrics: MetricArgs,
/// Add a new instance of a node.
///
@@ -59,7 +56,7 @@ pub struct NodeCommand<C: ChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs
/// - `HTTP_RPC_PORT`: default - `instance` + 1
/// - `WS_RPC_PORT`: default + `instance` * 2 - 2
/// - `IPC_PATH`: default + `-instance`
#[arg(long, value_name = "INSTANCE", global = true, value_parser = value_parser!(u16).range(..=200))]
#[arg(long, value_name = "INSTANCE", global = true, value_parser = value_parser!(u16).range(1..=200))]
pub instance: Option<u16>,
/// Sets all ports to unused, allowing the OS to choose random unused ports when sockets are
@@ -113,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,
@@ -148,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,
@@ -165,9 +166,10 @@ where
db,
dev,
pruning,
ext,
engine,
era,
static_files,
ext,
} = self;
// set up node config
@@ -187,6 +189,7 @@ where
pruning,
engine,
era,
static_files,
};
let data_dir = node_config.datadir();
@@ -213,6 +216,7 @@ impl<C: ChainSpecParser, Ext: clap::Args + fmt::Debug> NodeCommand<C, Ext> {
Some(&self.chain)
}
}
/// No Additional arguments
#[derive(Debug, Clone, Copy, Default, Args)]
#[non_exhaustive]
@@ -224,7 +228,7 @@ mod tests {
use reth_discv4::DEFAULT_DISCOVERY_PORT;
use reth_ethereum_cli::chainspec::{EthereumChainSpecParser, SUPPORTED_CHAINS};
use std::{
net::{IpAddr, Ipv4Addr},
net::{IpAddr, Ipv4Addr, SocketAddr},
path::Path,
};
@@ -285,15 +289,24 @@ mod tests {
fn parse_metrics_port() {
let cmd: NodeCommand<EthereumChainSpecParser> =
NodeCommand::try_parse_args_from(["reth", "--metrics", "9001"]).unwrap();
assert_eq!(cmd.metrics, Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001)));
assert_eq!(
cmd.metrics.prometheus,
Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001))
);
let cmd: NodeCommand<EthereumChainSpecParser> =
NodeCommand::try_parse_args_from(["reth", "--metrics", ":9001"]).unwrap();
assert_eq!(cmd.metrics, Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001)));
assert_eq!(
cmd.metrics.prometheus,
Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001))
);
let cmd: NodeCommand<EthereumChainSpecParser> =
NodeCommand::try_parse_args_from(["reth", "--metrics", "localhost:9001"]).unwrap();
assert_eq!(cmd.metrics, Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001)));
assert_eq!(
cmd.metrics.prometheus,
Some(SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 9001))
);
}
#[test]

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