Compare commits

...

91 Commits

Author SHA1 Message Date
Matthias Seitz
85f061d77d refactor: make capability mismatch detection a method on EngineCapabilities
- Add get_capability_mismatches and log_capability_mismatches as methods
- Add CapabilityMismatches result type for testability
- Add unit tests for capability comparison logic

Amp-Thread-ID: https://ampcode.com/threads/T-019bdac2-c961-75d1-a7fb-93f2190f2e1b
Co-authored-by: Amp <amp@ampcode.com>
2026-01-20 12:24:12 +01:00
tonisives
988ae03dd0 remove collect 2025-12-14 14:24:27 +07:00
tonisives
e739505b34 check capabilities
add test to verify capabilities are checked in the future

remove unused

update

use enum

update

update

update

remove tests
2025-12-13 12:47:25 +07: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
174 changed files with 5819 additions and 1556 deletions

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

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

View File

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

View File

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

View File

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

View File

@@ -13,12 +13,12 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
name: compact-codec
jobs:
compact-codec:
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
strategy:
matrix:
bin:
@@ -27,6 +27,7 @@ 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

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@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)'

View File

@@ -24,8 +24,7 @@ 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@v6
- name: Checkout hive tests
@@ -179,8 +178,7 @@ jobs:
- prepare-reth
- prepare-hive
name: run ${{ matrix.scenario.sim }}${{ matrix.scenario.limit && format(' - {0}', matrix.scenario.limit) }}
runs-on:
group: Reth
runs-on: depot-ubuntu-latest-16
permissions:
issues: write
steps:
@@ -247,8 +245,7 @@ jobs:
notify-on-error:
needs: test
if: failure()
runs-on:
group: Reth
runs-on: ubuntu-latest
steps:
- name: Slack Webhook Action
uses: rtCamp/action-slack-notify@v2

View File

@@ -14,6 +14,7 @@ on:
env:
CARGO_TERM_COLOR: always
SEED: rustethereumethereumrust
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -23,8 +24,7 @@ jobs:
test:
name: test / ${{ matrix.network }}
if: github.event_name != 'schedule'
runs-on:
group: Reth
runs-on: depot-ubuntu-latest-4
env:
RUST_BACKTRACE: 1
strategy:
@@ -38,6 +38,7 @@ jobs:
- 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
@@ -75,6 +76,7 @@ jobs:
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@nextest
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

View File

@@ -9,7 +9,7 @@ on:
push:
tags:
- '*'
- "*"
env:
CARGO_TERM_COLOR: always
@@ -32,8 +32,7 @@ jobs:
strategy:
fail-fast: false
name: run kurtosis
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
needs:
- prepare-reth
steps:
@@ -83,12 +82,10 @@ jobs:
kurtosis service logs -a op-devnet op-cl-2151908-2-op-node-op-reth-op-kurtosis
exit 1
notify-on-error:
needs: test
if: failure()
runs-on:
group: Reth
runs-on: ubuntu-latest
steps:
- name: Slack Webhook Action
uses: rtCamp/action-slack-notify@v2

View File

@@ -9,7 +9,7 @@ on:
push:
tags:
- '*'
- "*"
env:
CARGO_TERM_COLOR: always
@@ -30,8 +30,7 @@ jobs:
strategy:
fail-fast: false
name: run kurtosis
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
needs:
- prepare-reth
steps:
@@ -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

@@ -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:
@@ -26,6 +27,7 @@ jobs:
- 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,7 +42,7 @@ jobs:
clippy:
name: clippy
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
@@ -48,6 +50,7 @@ jobs:
- 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,7 +59,7 @@ jobs:
RUSTFLAGS: -D warnings
wasm:
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
@@ -65,6 +68,7 @@ jobs:
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,7 +79,7 @@ jobs:
.github/assets/check_wasm.sh
riscv:
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
@@ -84,6 +88,7 @@ jobs:
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
@@ -93,17 +98,18 @@ jobs:
crate-checks:
name: crate-checks (${{ matrix.partition }}/${{ matrix.total_partitions }})
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest-4
strategy:
matrix:
partition: [1, 2]
total_partitions: [2]
partition: [1, 2, 3]
total_partitions: [3]
timeout-minutes: 60
steps:
- 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
@@ -111,7 +117,7 @@ jobs:
msrv:
name: MSRV
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
@@ -124,6 +130,7 @@ jobs:
- 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
@@ -133,12 +140,13 @@ jobs:
docs:
name: docs
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest-4
timeout-minutes: 30
steps:
- 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
@@ -150,7 +158,7 @@ jobs:
fmt:
name: fmt
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
@@ -158,17 +166,19 @@ jobs:
- 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@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
@@ -177,12 +187,13 @@ jobs:
book:
name: book
runs-on: ubuntu-latest
runs-on: depot-ubuntu-latest
timeout-minutes: 30
steps:
- 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
@@ -232,31 +243,43 @@ jobs:
- 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
name: features (${{ matrix.partition }}/${{ matrix.total_partitions }})
runs-on: depot-ubuntu-latest
strategy:
matrix:
partition: [1, 2]
total_partitions: [2]
timeout-minutes: 30
steps:
- 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 \
--partition ${{ matrix.partition }}/${{ matrix.total_partitions }}
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@v6
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: rui314/setup-mold@v1
- uses: taiki-e/cache-cargo-install-action@v2
with:

View File

@@ -26,8 +26,7 @@ jobs:
prepare-reth:
if: github.repository == 'paradigmxyz/reth'
timeout-minutes: 45
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: mkdir artifacts

View File

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

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:
@@ -51,6 +52,7 @@ jobs:
steps:
- 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
@@ -104,6 +106,7 @@ jobs:
- 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: |

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,8 +23,7 @@ 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
@@ -32,6 +32,7 @@ jobs:
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

View File

@@ -9,6 +9,7 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -17,8 +18,7 @@ concurrency:
jobs:
sync:
name: sync (${{ matrix.chain.bin }})
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
env:
RUST_LOG: info,sync=error
RUST_BACKTRACE: 1
@@ -42,6 +42,7 @@ jobs:
- 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
@@ -42,6 +42,7 @@ jobs:
- 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:
@@ -47,6 +47,7 @@ jobs:
- 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,8 +66,7 @@ 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
@@ -93,6 +93,7 @@ jobs:
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: taiki-e/install-action@nextest
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
@@ -100,8 +101,7 @@ jobs:
doc:
name: doc tests
runs-on:
group: Reth
runs-on: depot-ubuntu-latest
env:
RUST_BACKTRACE: 1
timeout-minutes: 30
@@ -109,6 +109,7 @@ jobs:
- uses: actions/checkout@v6
- uses: rui314/setup-mold@v1
- uses: dtolnay/rust-toolchain@stable
- uses: mozilla-actions/sccache-action@v0.0.9
- uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true

View File

@@ -9,9 +9,12 @@ 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:
@@ -21,6 +24,7 @@ jobs:
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,7 +34,7 @@ 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:
@@ -40,6 +44,7 @@ jobs:
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

269
Cargo.lock generated
View File

@@ -97,9 +97,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "alloy-chains"
version = "0.2.20"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bc32535569185cbcb6ad5fa64d989a47bccb9a08e27284b1f2a3ccf16e6d010"
checksum = "1b9ebac8ff9c2f07667e1803dc777304337e160ce5153335beb45e8ec0751808"
dependencies = [
"alloy-primitives",
"alloy-rlp",
@@ -112,9 +112,9 @@ dependencies = [
[[package]]
name = "alloy-consensus"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6440213a22df93a87ed512d2f668e7dc1d62a05642d107f82d61edc9e12370"
checksum = "2e318e25fb719e747a7e8db1654170fc185024f3ed5b10f86c08d448a912f6e2"
dependencies = [
"alloy-eips",
"alloy-primitives",
@@ -140,9 +140,9 @@ dependencies = [
[[package]]
name = "alloy-consensus-any"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15d0bea09287942405c4f9d2a4f22d1e07611c2dbd9d5bf94b75366340f9e6e0"
checksum = "364380a845193a317bcb7a5398fc86cdb66c47ebe010771dde05f6869bf9e64a"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -155,9 +155,9 @@ dependencies = [
[[package]]
name = "alloy-contract"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d69af404f1d00ddb42f2419788fa87746a4cd13bab271916d7726fda6c792d94"
checksum = "08d39c80ffc806f27a76ed42f3351a455f3dc4f81d6ff92c8aad2cf36b7d3a34"
dependencies = [
"alloy-consensus",
"alloy-dyn-abi",
@@ -239,10 +239,22 @@ dependencies = [
]
[[package]]
name = "alloy-eips"
version = "1.1.2"
name = "alloy-eip7928"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4bd2c7ae05abcab4483ce821f12f285e01c0b33804e6883dd9ca1569a87ee2be"
checksum = "926b2c0d34e641cf8b17bf54ce50fda16715b9f68ad878fa6128bae410c6f890"
dependencies = [
"alloy-primitives",
"alloy-rlp",
"borsh",
"serde",
]
[[package]]
name = "alloy-eips"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4c4d7c5839d9f3a467900c625416b24328450c65702eb3d8caff8813e4d1d33"
dependencies = [
"alloy-eip2124",
"alloy-eip2930",
@@ -266,9 +278,9 @@ dependencies = [
[[package]]
name = "alloy-evm"
version = "0.24.2"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01be36ba6f5e6e62563b369e03ca529eac46aea50677f84655084b4750816574"
checksum = "3e7b4fb2418490bca9978e74208215ed5fcb21a10aba7eea487abaa60fd588db"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -288,9 +300,9 @@ dependencies = [
[[package]]
name = "alloy-genesis"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc47eaae86488b07ea8e20236184944072a78784a1f4993f8ec17b3aa5d08c21"
checksum = "1ba4b1be0988c11f0095a2380aa596e35533276b8fa6c9e06961bbfe0aebcac5"
dependencies = [
"alloy-eips",
"alloy-primitives",
@@ -329,9 +341,9 @@ dependencies = [
[[package]]
name = "alloy-json-rpc"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "003f46c54f22854a32b9cc7972660a476968008ad505427eabab49225309ec40"
checksum = "f72cf87cda808e593381fb9f005ffa4d2475552b7a6c5ac33d087bf77d82abd0"
dependencies = [
"alloy-primitives",
"alloy-sol-types",
@@ -344,9 +356,9 @@ dependencies = [
[[package]]
name = "alloy-network"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f4029954d9406a40979f3a3b46950928a0fdcfe3ea8a9b0c17490d57e8aa0e3"
checksum = "12aeb37b6f2e61b93b1c3d34d01ee720207c76fe447e2a2c217e433ac75b17f5"
dependencies = [
"alloy-consensus",
"alloy-consensus-any",
@@ -370,9 +382,9 @@ dependencies = [
[[package]]
name = "alloy-network-primitives"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7805124ad69e57bbae7731c9c344571700b2a18d351bda9e0eba521c991d1bcb"
checksum = "abd29ace62872083e30929cd9b282d82723196d196db589f3ceda67edcc05552"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -383,9 +395,9 @@ dependencies = [
[[package]]
name = "alloy-op-evm"
version = "0.24.2"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "231262d7e06000f3fb642d32d38ca75e09e78e04977c10be0a07a5ee2c869cfd"
checksum = "56afbe3c3b66435c7c3846fe639a60e4cdbc31b9263596eb2f382408b1b160c4"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -401,9 +413,9 @@ dependencies = [
[[package]]
name = "alloy-op-hardforks"
version = "0.4.4"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95ac97adaba4c26e17192d81f49186ac20c1e844e35a00e169c8d3d58bc84e6b"
checksum = "f96fb2fce4024ada5b2c11d4076acf778a0d3e4f011c6dfd2ffce6d0fcf84ee9"
dependencies = [
"alloy-chains",
"alloy-hardforks",
@@ -444,9 +456,9 @@ dependencies = [
[[package]]
name = "alloy-provider"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d369e12c92870d069e0c9dc5350377067af8a056e29e3badf8446099d7e00889"
checksum = "9b710636d7126e08003b8217e24c09f0cca0b46d62f650a841736891b1ed1fc1"
dependencies = [
"alloy-chains",
"alloy-consensus",
@@ -489,9 +501,9 @@ dependencies = [
[[package]]
name = "alloy-pubsub"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f77d20cdbb68a614c7a86b3ffef607b37d087bb47a03c58f4c3f8f99bc3ace3b"
checksum = "cdd4c64eb250a18101d22ae622357c6b505e158e9165d4c7974d59082a600c5e"
dependencies = [
"alloy-json-rpc",
"alloy-primitives",
@@ -533,9 +545,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-client"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "31c89883fe6b7381744cbe80fef638ac488ead4f1956a4278956a1362c71cd2e"
checksum = "d0882e72d2c1c0c79dcf4ab60a67472d3f009a949f774d4c17d0bdb669cfde05"
dependencies = [
"alloy-json-rpc",
"alloy-primitives",
@@ -559,9 +571,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64e279e6d40ee40fe8f76753b678d8d5d260cb276dc6c8a8026099b16d2b43f4"
checksum = "39cf1398cb33aacb139a960fa3d8cf8b1202079f320e77e952a0b95967bf7a9f"
dependencies = [
"alloy-primitives",
"alloy-rpc-types-engine",
@@ -572,9 +584,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-admin"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bcf50ccb65d29b8599f8f5e23dcac685f1d79459654c830cba381345760e901"
checksum = "65a583d2029b171301f5dcf122aa2ef443a65a373778ec76540d999691ae867d"
dependencies = [
"alloy-genesis",
"alloy-primitives",
@@ -584,9 +596,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-anvil"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e176c26fdd87893b6afeb5d92099d8f7e7a1fe11d6f4fe0883d6e33ac5f31ba"
checksum = "c3ce4c24e416bd0f17fceeb2f26cd8668df08fe19e1dc02f9d41c3b8ed1e93e0"
dependencies = [
"alloy-primitives",
"alloy-rpc-types-eth",
@@ -596,9 +608,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-any"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b43c1622aac2508d528743fd4cfdac1dea92d5a8fa894038488ff7edd0af0b32"
checksum = "6a63fb40ed24e4c92505f488f9dd256e2afaed17faa1b7a221086ebba74f4122"
dependencies = [
"alloy-consensus-any",
"alloy-rpc-types-eth",
@@ -607,9 +619,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-beacon"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1786681640d4c60f22b6b8376b0f3fa200360bf1c3c2cb913e6c97f51928eb1b"
checksum = "16633087e23d8d75161c3a59aa183203637b817a5a8d2f662f612ccb6d129af0"
dependencies = [
"alloy-eips",
"alloy-primitives",
@@ -627,9 +639,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-debug"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b2ca3a434a6d49910a7e8e51797eb25db42ef8a5578c52d877fcb26d0afe7bc"
checksum = "4936f579d9d10eae01772b2ab3497f9d568684f05f26f8175e12f9a1a2babc33"
dependencies = [
"alloy-primitives",
"derive_more",
@@ -639,9 +651,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-engine"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9c4c53a8b0905d931e7921774a1830609713bd3e8222347963172b03a3ecc68"
checksum = "4c60bdce3be295924122732b7ecd0b2495ce4790bedc5370ca7019c08ad3f26e"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -660,9 +672,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-eth"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed5fafb741c19b3cca4cdd04fa215c89413491f9695a3e928dee2ae5657f607e"
checksum = "9eae0c7c40da20684548cbc8577b6b7447f7bf4ddbac363df95e3da220e41e72"
dependencies = [
"alloy-consensus",
"alloy-consensus-any",
@@ -682,9 +694,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-mev"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a97bfc6d9b411c85bb08e1174ddd3e5d61b10d3bd13f529d6609f733cb2f6f"
checksum = "81c0dd81c24944cfbf45b5df7cd149d9cd3e354db81ccf08aa47e0e05be8ab97"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -697,9 +709,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-trace"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c55324323aa634b01bdecb2d47462a8dce05f5505b14a6e5db361eef16eda476"
checksum = "ef206a4b8d436fbb7cf2e6a61c692d11df78f9382becc3c9a283bd58e64f0583"
dependencies = [
"alloy-primitives",
"alloy-rpc-types-eth",
@@ -711,9 +723,9 @@ dependencies = [
[[package]]
name = "alloy-rpc-types-txpool"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96b1aa28effb6854be356ce92ed64cea3b323acd04c3f8bfb5126e2839698043"
checksum = "ecb5a795264a02222f9534435b8f40dcbd88de8e9d586647884aae24f389ebf2"
dependencies = [
"alloy-primitives",
"alloy-rpc-types-eth",
@@ -723,9 +735,9 @@ dependencies = [
[[package]]
name = "alloy-serde"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6f180c399ca7c1e2fe17ea58343910cad0090878a696ff5a50241aee12fc529"
checksum = "c0df1987ed0ff2d0159d76b52e7ddfc4e4fbddacc54d2fbee765e0d14d7c01b5"
dependencies = [
"alloy-primitives",
"arbitrary",
@@ -735,9 +747,9 @@ dependencies = [
[[package]]
name = "alloy-signer"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecc39ad2c0a3d2da8891f4081565780703a593f090f768f884049aa3aa929cbc"
checksum = "6ff69deedee7232d7ce5330259025b868c5e6a52fa8dffda2c861fb3a5889b24"
dependencies = [
"alloy-primitives",
"async-trait",
@@ -750,9 +762,9 @@ dependencies = [
[[package]]
name = "alloy-signer-local"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "930e17cb1e46446a193a593a3bfff8d0ecee4e510b802575ebe300ae2e43ef75"
checksum = "72cfe0be3ec5a8c1a46b2e5a7047ed41121d360d97f4405bb7c1c784880c86cb"
dependencies = [
"alloy-consensus",
"alloy-network",
@@ -839,9 +851,9 @@ dependencies = [
[[package]]
name = "alloy-transport"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cae82426d98f8bc18f53c5223862907cac30ab8fc5e4cd2bb50808e6d3ab43d8"
checksum = "be98b07210d24acf5b793c99b759e9a696e4a2e67593aec0487ae3b3e1a2478c"
dependencies = [
"alloy-json-rpc",
"auto_impl",
@@ -862,9 +874,9 @@ dependencies = [
[[package]]
name = "alloy-transport-http"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90aa6825760905898c106aba9c804b131816a15041523e80b6d4fe7af6380ada"
checksum = "4198a1ee82e562cab85e7f3d5921aab725d9bd154b6ad5017f82df1695877c97"
dependencies = [
"alloy-json-rpc",
"alloy-transport",
@@ -877,9 +889,9 @@ dependencies = [
[[package]]
name = "alloy-transport-ipc"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ace83a4a6bb896e5894c3479042e6ba78aa5271dde599aa8c36a021d49cc8cc"
checksum = "d8db249779ebc20dc265920c7e706ed0d31dbde8627818d1cbde60919b875bb0"
dependencies = [
"alloy-json-rpc",
"alloy-pubsub",
@@ -897,9 +909,9 @@ dependencies = [
[[package]]
name = "alloy-transport-ws"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "86c9ab4c199e3a8f3520b60ba81aa67bb21fed9ed0d8304e0569094d0758a56f"
checksum = "5ad2344a12398d7105e3722c9b7a7044ea837128e11d453604dec6e3731a86e2"
dependencies = [
"alloy-pubsub",
"alloy-transport",
@@ -935,9 +947,9 @@ dependencies = [
[[package]]
name = "alloy-tx-macros"
version = "1.1.2"
version = "1.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae109e33814b49fc0a62f2528993aa8a2dd346c26959b151f05441dc0b9da292"
checksum = "333544408503f42d7d3792bfc0f7218b643d968a03d2c0ed383ae558fb4a76d0"
dependencies = [
"darling 0.21.3",
"proc-macro2",
@@ -1627,15 +1639,15 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
[[package]]
name = "bitcoin-io"
version = "0.1.3"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf"
checksum = "2dee39a0ee5b4095224a0cfc6bf4cc1baf0f9624b96b367e53b66d974e51d953"
[[package]]
name = "bitcoin_hashes"
version = "0.14.0"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16"
checksum = "26ec84b80c482df901772e931a9a681e26a1b9ee2302edeff23cb30328745c8b"
dependencies = [
"bitcoin-io",
"hex-conservative",
@@ -2471,9 +2483,9 @@ dependencies = [
[[package]]
name = "convert_case"
version = "0.7.1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
dependencies = [
"unicode-segmentation",
]
@@ -2978,22 +2990,23 @@ dependencies = [
[[package]]
name = "derive_more"
version = "2.0.1"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.0.1"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version 0.4.1",
"syn 2.0.111",
"unicode-xid",
]
@@ -3670,6 +3683,17 @@ dependencies = [
"tracing",
]
[[package]]
name = "example-custom-rpc-middleware"
version = "0.0.0"
dependencies = [
"clap",
"jsonrpsee",
"reth-ethereum",
"tower",
"tracing",
]
[[package]]
name = "example-db-access"
version = "0.0.0"
@@ -3981,9 +4005,9 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flate2"
version = "1.1.5"
version = "1.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
checksum = "a2152dbcb980c05735e2a651d96011320a949eb31a0c8b38b72645ce97dec676"
dependencies = [
"crc32fast",
"miniz_oxide",
@@ -4277,9 +4301,9 @@ dependencies = [
[[package]]
name = "git2"
version = "0.20.2"
version = "0.20.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2deb07a133b1520dc1a5690e9bd08950108873d7ed5de38dcc74d3b5ebffa110"
checksum = "3e2b37e2f62729cdada11f0e6b3b6fe383c69c29fc619e391223e12856af308c"
dependencies = [
"bitflags 2.10.0",
"libc",
@@ -4704,9 +4728,9 @@ dependencies = [
[[package]]
name = "hyper-util"
version = "0.1.18"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52e9a2a24dc5c6821e71a7030e1e14b7b632acac55c40e9d2e082c621261bb56"
checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -5416,15 +5440,15 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
[[package]]
name = "libc"
version = "0.2.177"
version = "0.2.178"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976"
checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091"
[[package]]
name = "libgit2-sys"
version = "0.18.2+1.9.1"
version = "0.18.3+1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c42fe03df2bd3c53a3a9c7317ad91d80c81cd1fb0caec8d7cc4cd2bfa10c222"
checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487"
dependencies = [
"cc",
"libc",
@@ -5569,9 +5593,9 @@ dependencies = [
[[package]]
name = "log"
version = "0.4.28"
version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
[[package]]
name = "loom"
@@ -5839,9 +5863,9 @@ dependencies = [
[[package]]
name = "mio"
version = "1.1.0"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
dependencies = [
"libc",
"log",
@@ -6187,9 +6211,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "op-alloy"
version = "0.22.4"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3b13412d297c1f9341f678b763750b120a73ffe998fa54a94d6eda98449e7ca"
checksum = "5f8cef53b364f406ed2be3a447b2d8f2f18b07a6ff1255c287debb4cda68095b"
dependencies = [
"op-alloy-consensus",
"op-alloy-network",
@@ -6200,9 +6224,9 @@ dependencies = [
[[package]]
name = "op-alloy-consensus"
version = "0.22.4"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "726da827358a547be9f1e37c2a756b9e3729cb0350f43408164794b370cad8ae"
checksum = "736381a95471d23e267263cfcee9e1d96d30b9754a94a2819148f83379de8a86"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -6226,9 +6250,9 @@ checksum = "a79f352fc3893dcd670172e615afef993a41798a1d3fc0db88a3e60ef2e70ecc"
[[package]]
name = "op-alloy-network"
version = "0.22.4"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f63f27e65be273ec8fcb0b6af0fd850b550979465ab93423705ceb3dfddbd2ab"
checksum = "4034183dca6bff6632e7c24c92e75ff5f0eabb58144edb4d8241814851334d47"
dependencies = [
"alloy-consensus",
"alloy-network",
@@ -6242,9 +6266,9 @@ dependencies = [
[[package]]
name = "op-alloy-provider"
version = "0.22.4"
version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a71456699aa256dc20119736422ad9a44da8b9585036117afb936778122093b9"
checksum = "9f1c952895ad45087d35d323e3fb73c0b5de7c6852494d81ebe997030366196a"
dependencies = [
"alloy-network",
"alloy-primitives",
@@ -6257,9 +6281,9 @@ dependencies = [
[[package]]
name = "op-alloy-rpc-jsonrpsee"
version = "0.22.4"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ef9114426b16172254555aad34a8ea96c01895e40da92f5d12ea680a1baeaa7"
checksum = "c1c820ef9c802ebc732281a940bfb6ac2345af4d9fff041cbb64b4b546676686"
dependencies = [
"alloy-primitives",
"jsonrpsee",
@@ -6267,9 +6291,9 @@ dependencies = [
[[package]]
name = "op-alloy-rpc-types"
version = "0.22.4"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "562dd4462562c41f9fdc4d860858c40e14a25df7f983ae82047f15f08fce4d19"
checksum = "ddd87c6b9e5b6eee8d6b76f41b04368dca0e9f38d83338e5b00e730c282098a4"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -6287,9 +6311,9 @@ dependencies = [
[[package]]
name = "op-alloy-rpc-types-engine"
version = "0.22.4"
version = "0.23.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8f24b8cb66e4b33e6c9e508bf46b8ecafc92eadd0b93fedd306c0accb477657"
checksum = "77727699310a18cdeed32da3928c709e2704043b6584ed416397d5da65694efc"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -6303,6 +6327,7 @@ dependencies = [
"ethereum_ssz_derive",
"op-alloy-consensus",
"serde",
"sha2",
"snap",
"thiserror 2.0.17",
]
@@ -6792,7 +6817,7 @@ version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
dependencies = [
"toml_edit 0.23.7",
"toml_edit 0.23.9",
]
[[package]]
@@ -8195,6 +8220,7 @@ name = "reth-engine-tree"
version = "1.9.3"
dependencies = [
"alloy-consensus",
"alloy-eip7928",
"alloy-eips",
"alloy-evm",
"alloy-primitives",
@@ -9219,6 +9245,7 @@ dependencies = [
"alloy-sol-types",
"eyre",
"futures",
"jsonrpsee-core",
"rand 0.9.2",
"reth-chainspec",
"reth-db",
@@ -9254,6 +9281,7 @@ dependencies = [
"serde",
"serde_json",
"similar-asserts",
"tempfile",
"tokio",
]
@@ -10144,6 +10172,7 @@ dependencies = [
"reth-db-api",
"reth-engine-primitives",
"reth-errors",
"reth-ethereum-engine-primitives",
"reth-ethereum-primitives",
"reth-evm",
"reth-evm-ethereum",
@@ -10206,6 +10235,9 @@ dependencies = [
"reth-network-peers",
"reth-rpc-eth-api",
"reth-trie-common",
"serde",
"serde_json",
"tokio",
]
[[package]]
@@ -10270,6 +10302,7 @@ dependencies = [
"reth-rpc-server-types",
"reth-storage-api",
"reth-tasks",
"reth-tokio-util",
"reth-tracing",
"reth-transaction-pool",
"serde",
@@ -11163,9 +11196,9 @@ dependencies = [
[[package]]
name = "revm-inspectors"
version = "0.33.1"
version = "0.33.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c93974333e7acc4b2dc024b10def99707f7375a4d53db7a7f8351722d25673f"
checksum = "01def7351cd9af844150b8e88980bcd11304f33ce23c3d7c25f2a8dab87c1345"
dependencies = [
"alloy-primitives",
"alloy-rpc-types-eth",
@@ -12787,9 +12820,9 @@ dependencies = [
[[package]]
name = "toml_edit"
version = "0.23.7"
version = "0.23.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d"
checksum = "5d7cbc3b4b49633d57a0509303158ca50de80ae32c265093b24c414705807832"
dependencies = [
"indexmap 2.12.1",
"toml_datetime 0.7.3",
@@ -13303,9 +13336,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]]
name = "uuid"
version = "1.18.1"
version = "1.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
checksum = "e2e054861b4bd027cd373e18e8d8d8e6548085000e41290d95ce0c373a654b4a"
dependencies = [
"getrandom 0.3.4",
"js-sys",
@@ -14282,18 +14315,18 @@ dependencies = [
[[package]]
name = "zerocopy"
version = "0.8.30"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ea879c944afe8a2b25fef16bb4ba234f47c694565e97383b36f3a878219065c"
checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.8.30"
version = "0.8.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf955aa904d6040f70dc8e9384444cb1030aed272ba3cb09bbc4ab9e7c1f34f5"
checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -153,6 +153,7 @@ members = [
"examples/custom-node-components/",
"examples/custom-payload-builder/",
"examples/custom-rlpx-subprotocol",
"examples/custom-rpc-middleware",
"examples/custom-node",
"examples/db-access",
"examples/engine-api-access",
@@ -375,11 +376,11 @@ reth-era-utils = { path = "crates/era-utils" }
reth-errors = { path = "crates/errors" }
reth-eth-wire = { path = "crates/net/eth-wire" }
reth-eth-wire-types = { path = "crates/net/eth-wire-types" }
reth-ethereum-payload-builder = { path = "crates/ethereum/payload" }
reth-ethereum-cli = { path = "crates/ethereum/cli", default-features = false }
reth-ethereum-consensus = { path = "crates/ethereum/consensus", default-features = false }
reth-ethereum-engine-primitives = { path = "crates/ethereum/engine-primitives", default-features = false }
reth-ethereum-forks = { path = "crates/ethereum/hardforks", default-features = false }
reth-ethereum-payload-builder = { path = "crates/ethereum/payload" }
reth-ethereum-primitives = { path = "crates/ethereum/primitives", default-features = false }
reth-ethereum = { path = "crates/ethereum/reth" }
reth-etl = { path = "crates/etl" }
@@ -480,13 +481,14 @@ 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.1"
revm-inspectors = "0.33.2"
# eth
alloy-chains = { version = "0.2.5", default-features = false }
alloy-dyn-abi = "1.4.1"
alloy-eip2124 = { version = "0.2.0", default-features = false }
alloy-evm = { version = "0.24.1", default-features = false }
alloy-eip7928 = { version = "0.1.0" }
alloy-evm = { version = "0.25.1", default-features = false }
alloy-primitives = { version = "1.4.1", default-features = false, features = ["map-foldhash"] }
alloy-rlp = { version = "0.3.10", default-features = false, features = ["core-net"] }
alloy-sol-macro = "1.4.1"
@@ -495,42 +497,42 @@ alloy-trie = { version = "0.9.1", default-features = false }
alloy-hardforks = "0.4.5"
alloy-consensus = { version = "1.1.2", default-features = false }
alloy-contract = { version = "1.1.2", default-features = false }
alloy-eips = { version = "1.1.2", default-features = false }
alloy-genesis = { version = "1.1.2", default-features = false }
alloy-json-rpc = { version = "1.1.2", default-features = false }
alloy-network = { version = "1.1.2", default-features = false }
alloy-network-primitives = { version = "1.1.2", default-features = false }
alloy-provider = { version = "1.1.2", features = ["reqwest", "debug-api"], default-features = false }
alloy-pubsub = { version = "1.1.2", default-features = false }
alloy-rpc-client = { version = "1.1.2", default-features = false }
alloy-rpc-types = { version = "1.1.2", features = ["eth"], default-features = false }
alloy-rpc-types-admin = { version = "1.1.2", default-features = false }
alloy-rpc-types-anvil = { version = "1.1.2", default-features = false }
alloy-rpc-types-beacon = { version = "1.1.2", default-features = false }
alloy-rpc-types-debug = { version = "1.1.2", default-features = false }
alloy-rpc-types-engine = { version = "1.1.2", default-features = false }
alloy-rpc-types-eth = { version = "1.1.2", default-features = false }
alloy-rpc-types-mev = { version = "1.1.2", default-features = false }
alloy-rpc-types-trace = { version = "1.1.2", default-features = false }
alloy-rpc-types-txpool = { version = "1.1.2", default-features = false }
alloy-serde = { version = "1.1.2", default-features = false }
alloy-signer = { version = "1.1.2", default-features = false }
alloy-signer-local = { version = "1.1.2", default-features = false }
alloy-transport = { version = "1.1.2" }
alloy-transport-http = { version = "1.1.2", features = ["reqwest-rustls-tls"], default-features = false }
alloy-transport-ipc = { version = "1.1.2", default-features = false }
alloy-transport-ws = { version = "1.1.2", 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.24.1", default-features = false }
alloy-op-evm = { version = "0.25.0", default-features = false }
alloy-op-hardforks = "0.4.4"
op-alloy-rpc-types = { version = "0.22.4", default-features = false }
op-alloy-rpc-types-engine = { version = "0.22.4", default-features = false }
op-alloy-network = { version = "0.22.4", default-features = false }
op-alloy-consensus = { version = "0.22.4", default-features = false }
op-alloy-rpc-jsonrpsee = { version = "0.22.4", default-features = false }
op-alloy-rpc-types = { version = "0.23.1", default-features = false }
op-alloy-rpc-types-engine = { version = "0.23.1", default-features = false }
op-alloy-network = { version = "0.23.1", default-features = false }
op-alloy-consensus = { version = "0.23.1", default-features = false }
op-alloy-rpc-jsonrpsee = { version = "0.23.1", default-features = false }
op-alloy-flz = { version = "0.13.1", default-features = false }
# misc

View File

@@ -523,8 +523,3 @@ pr:
make test
check-features:
cargo hack check \
--package reth-codecs \
--package reth-primitives-traits \
--package reth-primitives \
--feature-powerset

View File

@@ -506,8 +506,8 @@ async fn run_warmup_phase(
// Build additional args with conditional --debug.startup-sync-state-idle flag
let additional_args = args.build_additional_args("warmup", args.baseline_args.as_ref());
// Start reth node for warmup
let mut node_process =
// Start reth node for warmup (command is not stored for warmup phase)
let (mut node_process, _warmup_command) =
node_manager.start_node(&binary_path, warmup_ref, "warmup", &additional_args).await?;
// Wait for node to be ready and get its current tip
@@ -607,8 +607,8 @@ async fn run_benchmark_workflow(
// Build additional args with conditional --debug.startup-sync-state-idle flag
let additional_args = args.build_additional_args(ref_type, base_args_str);
// Start reth node
let mut node_process =
// Start reth node and capture the command for reporting
let (mut node_process, reth_command) =
node_manager.start_node(&binary_path, git_ref, ref_type, &additional_args).await?;
// Wait for node to be ready and get its current tip (wherever it is)
@@ -645,8 +645,9 @@ async fn run_benchmark_workflow(
// Store results for comparison
comparison_generator.add_ref_results(ref_type, &output_dir)?;
// Set the benchmark run timestamps
// Set the benchmark run timestamps and reth command
comparison_generator.set_ref_timestamps(ref_type, benchmark_start, benchmark_end)?;
comparison_generator.set_ref_command(ref_type, reth_command)?;
info!("Completed {} reference benchmark", ref_type);
}

View File

@@ -21,6 +21,8 @@ pub(crate) struct ComparisonGenerator {
feature_ref_name: String,
baseline_results: Option<BenchmarkResults>,
feature_results: Option<BenchmarkResults>,
baseline_command: Option<String>,
feature_command: Option<String>,
}
/// Represents the results from a single benchmark run
@@ -89,6 +91,7 @@ pub(crate) struct RefInfo {
pub summary: BenchmarkSummary,
pub start_timestamp: Option<DateTime<Utc>>,
pub end_timestamp: Option<DateTime<Utc>>,
pub reth_command: Option<String>,
}
/// Summary of the comparison between references.
@@ -142,6 +145,8 @@ impl ComparisonGenerator {
feature_ref_name: args.feature_ref.clone(),
baseline_results: None,
feature_results: None,
baseline_command: None,
feature_command: None,
}
}
@@ -206,6 +211,21 @@ impl ComparisonGenerator {
Ok(())
}
/// Set the reth command for a reference
pub(crate) fn set_ref_command(&mut self, ref_type: &str, command: String) -> Result<()> {
match ref_type {
"baseline" => {
self.baseline_command = Some(command);
}
"feature" => {
self.feature_command = Some(command);
}
_ => return Err(eyre!("Unknown reference type: {}", ref_type)),
}
Ok(())
}
/// Generate the final comparison report
pub(crate) async fn generate_comparison_report(&self) -> Result<()> {
info!("Generating comparison report...");
@@ -230,12 +250,14 @@ impl ComparisonGenerator {
summary: baseline.summary.clone(),
start_timestamp: baseline.start_timestamp,
end_timestamp: baseline.end_timestamp,
reth_command: self.baseline_command.clone(),
},
feature: RefInfo {
ref_name: feature.ref_name.clone(),
summary: feature.summary.clone(),
start_timestamp: feature.start_timestamp,
end_timestamp: feature.end_timestamp,
reth_command: self.feature_command.clone(),
},
comparison_summary,
per_block_comparisons,
@@ -599,6 +621,9 @@ impl ComparisonGenerator {
end.format("%Y-%m-%d %H:%M:%S UTC")
);
}
if let Some(ref cmd) = report.baseline.reth_command {
println!(" Command: {}", cmd);
}
println!();
println!("Feature Summary:");
@@ -628,6 +653,9 @@ impl ComparisonGenerator {
end.format("%Y-%m-%d %H:%M:%S UTC")
);
}
if let Some(ref cmd) = report.feature.reth_command {
println!(" Command: {}", cmd);
}
println!();
}
}

View File

@@ -240,19 +240,24 @@ impl NodeManager {
}
/// Start a reth node using the specified binary path and return the process handle
/// along with the formatted reth command string for reporting.
pub(crate) async fn start_node(
&mut self,
binary_path: &std::path::Path,
_git_ref: &str,
ref_type: &str,
additional_args: &[String],
) -> Result<tokio::process::Child> {
) -> Result<(tokio::process::Child, String)> {
// Store the binary path for later use (e.g., in unwind_to_block)
self.binary_path = Some(binary_path.to_path_buf());
let binary_path_str = binary_path.to_string_lossy();
let (reth_args, _) = self.build_reth_args(&binary_path_str, additional_args, 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);
@@ -346,7 +351,7 @@ impl NodeManager {
// Give the node a moment to start up
sleep(Duration::from_secs(5)).await;
Ok(child)
Ok((child, reth_command))
}
/// Wait for the node to be ready and return its current tip

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

@@ -18,7 +18,7 @@ use reth_primitives_traits::{
};
use reth_storage_api::StateProviderBox;
use reth_trie::{updates::TrieUpdatesSorted, HashedPostStateSorted, TrieInputSorted};
use std::{collections::BTreeMap, sync::Arc, time::Instant};
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.
@@ -634,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();
@@ -646,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):

View File

@@ -8,12 +8,17 @@ use reth_db::{
RawDupSort,
};
use reth_db_api::{
table::{Decompress, DupSort, Table},
tables, RawKey, RawTable, Receipts, TableViewer, Transactions,
cursor::{DbCursorRO, DbDupCursorRO},
database::Database,
table::{Compress, Decompress, DupSort, Table},
tables,
transaction::DbTx,
RawKey, RawTable, Receipts, TableViewer, Transactions,
};
use reth_db_common::DbTool;
use reth_node_api::{HeaderTy, ReceiptTy, TxTy};
use reth_node_builder::NodeTypesWithDB;
use reth_primitives_traits::ValueWithSubKey;
use reth_provider::{providers::ProviderNodeTypes, StaticFileProviderFactory};
use reth_static_file_types::StaticFileSegment;
use tracing::error;
@@ -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,8 +74,8 @@ impl Command {
/// Execute `db get` command
pub fn execute<N: ProviderNodeTypes>(self, tool: &DbTool<N>) -> eyre::Result<()> {
match self.subcommand {
Subcommand::Mdbx { table, key, subkey, raw } => {
table.view(&GetValueViewer { tool, key, subkey, raw })?
Subcommand::Mdbx { table, key, subkey, end_key, end_subkey, raw } => {
table.view(&GetValueViewer { tool, key, subkey, end_key, end_subkey, raw })?
}
Subcommand::StaticFile { segment, key, raw } => {
let (key, mask): (u64, _) = match segment {
@@ -154,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,
}
@@ -163,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

@@ -91,6 +91,9 @@ impl Command {
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

View File

@@ -110,7 +110,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
static_file_provider.commit()?;
} else if last_block_number > 0 && last_block_number < header.number() {
return Err(eyre::eyre!(
"Data directory should be empty when calling init-state with --without-evm-history."
"Data directory should be empty when calling init-state with --without-evm."
));
}
}

View File

@@ -531,8 +531,12 @@ impl PruneConfig {
self.segments.receipts.is_some() || !self.segments.receipts_log_filter.is_empty()
}
/// Merges another `PruneConfig` into this one, taking values from the other config if and only
/// if the corresponding value in this config is not set.
/// Merges values from `other` into `self`.
/// - `Option<PruneMode>` fields: set from `other` only if `self` is `None`.
/// - `block_interval`: set from `other` only if `self.block_interval ==
/// DEFAULT_BLOCK_INTERVAL`.
/// - `merkle_changesets`: always set from `other`.
/// - `receipts_log_filter`: set from `other` only if `self` is empty and `other` is non-empty.
pub fn merge(&mut self, other: Self) {
let Self {
block_interval,
@@ -561,7 +565,7 @@ impl PruneConfig {
self.segments.account_history = self.segments.account_history.or(account_history);
self.segments.storage_history = self.segments.storage_history.or(storage_history);
self.segments.bodies_history = self.segments.bodies_history.or(bodies_history);
// Merkle changesets is not optional, so we just replace it if provided
// Merkle changesets is not optional; always take the value from `other`
self.segments.merkle_changesets = merkle_changesets;
if self.segments.receipts_log_filter.0.is_empty() && !receipts_log_filter.0.is_empty() {

View File

@@ -327,7 +327,7 @@ pub fn validate_against_parent_eip1559_base_fee<ChainSpec: EthChainSpec + Ethere
Ok(())
}
/// Validates the timestamp against the parent to make sure it is in the past.
/// Validates that the block timestamp is greater than the parent block timestamp.
#[inline]
pub fn validate_against_parent_timestamp<H: BlockHeader>(
header: &H,

View File

@@ -1,5 +1,5 @@
use crate::BlockProvider;
use alloy_provider::{Network, Provider, ProviderBuilder};
use alloy_provider::{ConnectionConfig, Network, Provider, ProviderBuilder, WebSocketConfig};
use alloy_transport::TransportResult;
use futures::{Stream, StreamExt};
use reth_node_api::Block;
@@ -25,7 +25,19 @@ impl<N: Network, PrimitiveBlock> RpcBlockProvider<N, PrimitiveBlock> {
convert: impl Fn(N::BlockResponse) -> PrimitiveBlock + Send + Sync + 'static,
) -> eyre::Result<Self> {
Ok(Self {
provider: Arc::new(ProviderBuilder::default().connect(rpc_url).await?),
provider: Arc::new(
ProviderBuilder::default()
.connect_with_config(
rpc_url,
ConnectionConfig::default().with_max_retries(u32::MAX).with_ws_config(
WebSocketConfig::default()
// allow larger messages/frames for big blocks
.max_frame_size(Some(128 * 1024 * 1024))
.max_message_size(Some(128 * 1024 * 1024)),
),
)
.await?,
),
url: rpc_url.to_string(),
convert: Arc::new(convert),
})

View File

@@ -1,5 +1,7 @@
//! Engine tree configuration.
use alloy_eips::merge::EPOCH_SLOTS;
/// Triggers persistence when the number of canonical blocks in memory exceeds this threshold.
pub const DEFAULT_PERSISTENCE_THRESHOLD: u64 = 2;
@@ -40,7 +42,7 @@ pub const DEFAULT_RESERVED_CPU_CORES: usize = 1;
/// Default maximum concurrency for prewarm task.
pub const DEFAULT_PREWARM_MAX_CONCURRENCY: usize = 16;
const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = 256;
const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = EPOCH_SLOTS as u32 * 2;
const DEFAULT_MAX_INVALID_HEADER_CACHE_LENGTH: u32 = 256;
const DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE: usize = 4;
const DEFAULT_CROSS_BLOCK_CACHE_SIZE: u64 = 4 * 1024 * 1024 * 1024;
@@ -89,6 +91,8 @@ pub struct TreeConfig {
/// Whether to always compare trie updates from the state root task to the trie updates from
/// the regular state root calculation.
always_compare_trie_updates: bool,
/// Whether to disable state cache.
disable_state_cache: bool,
/// Whether to disable parallel prewarming.
disable_prewarming: bool,
/// Whether to disable the parallel sparse trie state root algorithm.
@@ -143,6 +147,7 @@ impl Default for TreeConfig {
max_execute_block_batch_size: DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE,
legacy_state_root: false,
always_compare_trie_updates: false,
disable_state_cache: false,
disable_prewarming: false,
disable_parallel_sparse_trie: false,
state_provider_metrics: false,
@@ -173,6 +178,7 @@ impl TreeConfig {
max_execute_block_batch_size: usize,
legacy_state_root: bool,
always_compare_trie_updates: bool,
disable_state_cache: bool,
disable_prewarming: bool,
disable_parallel_sparse_trie: bool,
state_provider_metrics: bool,
@@ -197,6 +203,7 @@ impl TreeConfig {
max_execute_block_batch_size,
legacy_state_root,
always_compare_trie_updates,
disable_state_cache,
disable_prewarming,
disable_parallel_sparse_trie,
state_provider_metrics,
@@ -271,7 +278,12 @@ impl TreeConfig {
self.disable_parallel_sparse_trie
}
/// Returns whether or not parallel prewarming should be used.
/// Returns whether or not state cache is disabled.
pub const fn disable_state_cache(&self) -> bool {
self.disable_state_cache
}
/// Returns whether or not parallel prewarming is disabled.
pub const fn disable_prewarming(&self) -> bool {
self.disable_prewarming
}
@@ -363,6 +375,12 @@ impl TreeConfig {
self
}
/// Setter for whether to disable state cache.
pub const fn without_state_cache(mut self, disable_state_cache: bool) -> Self {
self.disable_state_cache = disable_state_cache;
self
}
/// Setter for whether to disable parallel prewarming.
pub const fn without_prewarming(mut self, disable_prewarming: bool) -> Self {
self.disable_prewarming = disable_prewarming;

View File

@@ -39,6 +39,7 @@ reth-trie.workspace = true
alloy-evm.workspace = true
alloy-consensus.workspace = true
alloy-eips.workspace = true
alloy-eip7928.workspace = true
alloy-primitives.workspace = true
alloy-rlp.workspace = true
alloy-rpc-types-engine.workspace = true

View File

@@ -229,9 +229,15 @@ fn bench_state_root(c: &mut Criterion) {
black_box({
let mut handle = payload_processor.spawn(
Default::default(),
core::iter::empty::<
Result<Recovered<TransactionSigned>, core::convert::Infallible>,
>(),
(
core::iter::empty::<
Result<
Recovered<TransactionSigned>,
core::convert::Infallible,
>,
>(),
std::convert::identity,
),
StateProviderBuilder::new(provider.clone(), genesis_hash, None),
OverlayStateProviderFactory::new(provider),
&TreeConfig::default(),

View File

@@ -146,17 +146,26 @@ impl<S: StateProvider> StateProvider for CachedStateProvider<S> {
storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
match self.caches.get_storage(&account, &storage_key) {
SlotStatus::NotCached => {
self.metrics.storage_cache_misses.increment(1);
(SlotStatus::NotCached, maybe_cache) => {
let final_res = self.state_provider.storage(account, storage_key)?;
self.caches.insert_storage(account, storage_key, final_res);
let account_cache = maybe_cache.unwrap_or_default();
account_cache.insert_storage(storage_key, final_res);
// we always need to insert the value to update the weights.
// Note: there exists a race when the storage cache did not exist yet and two
// consumers looking up the a storage value for this account for the first time,
// however we can assume that this will only happen for the very first (mostlikely
// the same) value, and don't expect that this will accidentally
// replace an account storage cache with additional values.
self.caches.insert_storage_cache(account, account_cache);
self.metrics.storage_cache_misses.increment(1);
Ok(final_res)
}
SlotStatus::Empty => {
(SlotStatus::Empty, _) => {
self.metrics.storage_cache_hits.increment(1);
Ok(None)
}
SlotStatus::Value(value) => {
(SlotStatus::Value(value), _) => {
self.metrics.storage_cache_hits.increment(1);
Ok(Some(value))
}
@@ -311,18 +320,28 @@ pub(crate) struct ExecutionCache {
impl ExecutionCache {
/// Get storage value from hierarchical cache.
///
/// Returns a `SlotStatus` indicating whether:
/// - `NotCached`: The account's storage cache doesn't exist
/// - `Empty`: The slot exists in the account's cache but is empty
/// - `Value`: The slot exists and has a specific value
pub(crate) fn get_storage(&self, address: &Address, key: &StorageKey) -> SlotStatus {
/// Returns a tuple of:
/// - `SlotStatus` indicating whether:
/// - `NotCached`: The account's storage cache doesn't exist
/// - `Empty`: The slot exists in the account's cache but is empty
/// - `Value`: The slot exists and has a specific value
/// - `Option<Arc<AccountStorageCache>>`: The account's storage cache if it exists
pub(crate) fn get_storage(
&self,
address: &Address,
key: &StorageKey,
) -> (SlotStatus, Option<Arc<AccountStorageCache>>) {
match self.storage_cache.get(address) {
None => SlotStatus::NotCached,
Some(account_cache) => account_cache.get_storage(key),
None => (SlotStatus::NotCached, None),
Some(account_cache) => {
let status = account_cache.get_storage(key);
(status, Some(account_cache))
}
}
}
/// Insert storage value into hierarchical cache
#[cfg(test)]
pub(crate) fn insert_storage(
&self,
address: Address,
@@ -351,6 +370,15 @@ impl ExecutionCache {
self.storage_cache.insert(address, account_cache);
}
/// Inserts the [`AccountStorageCache`].
pub(crate) fn insert_storage_cache(
&self,
address: Address,
storage_cache: Arc<AccountStorageCache>,
) {
self.storage_cache.insert(address, storage_cache);
}
/// Invalidate storage for specific account
pub(crate) fn invalidate_account_storage(&self, address: &Address) {
self.storage_cache.invalidate(address);
@@ -800,7 +828,7 @@ mod tests {
caches.insert_storage(address, storage_key, Some(storage_value));
// check that the storage returns the cached value
let slot_status = caches.get_storage(&address, &storage_key);
let (slot_status, _) = caches.get_storage(&address, &storage_key);
assert_eq!(slot_status, SlotStatus::Value(storage_value));
}
@@ -814,7 +842,7 @@ mod tests {
let caches = ExecutionCacheBuilder::default().build_caches(1000);
// check that the storage is not cached
let slot_status = caches.get_storage(&address, &storage_key);
let (slot_status, _) = caches.get_storage(&address, &storage_key);
assert_eq!(slot_status, SlotStatus::NotCached);
}
@@ -830,7 +858,7 @@ mod tests {
caches.insert_storage(address, storage_key, None);
// check that the storage is empty
let slot_status = caches.get_storage(&address, &storage_key);
let (slot_status, _) = caches.get_storage(&address, &storage_key);
assert_eq!(slot_status, SlotStatus::Empty);
}

View File

@@ -22,7 +22,7 @@ const NANOS_PER_SEC: u32 = 1_000_000_000;
/// An atomic version of [`Duration`], using an [`AtomicU64`] to store the total nanoseconds in the
/// duration.
#[derive(Default)]
#[derive(Debug, Default)]
pub(crate) struct AtomicDuration {
/// The nanoseconds part of the duration
///
@@ -59,7 +59,8 @@ impl AtomicDuration {
}
/// A wrapper of a state provider and latency metrics.
pub(crate) struct InstrumentedStateProvider<S> {
#[derive(Debug)]
pub struct InstrumentedStateProvider<S> {
/// The state provider
state_provider: S,
@@ -80,11 +81,12 @@ impl<S> InstrumentedStateProvider<S>
where
S: StateProvider,
{
/// Creates a new [`InstrumentedStateProvider`] from a state provider
pub(crate) fn from_state_provider(state_provider: S) -> Self {
/// Creates a new [`InstrumentedStateProvider`] from a state provider with the provided label
/// for metrics.
pub fn from_state_provider(state_provider: S, source: &'static str) -> Self {
Self {
state_provider,
metrics: StateProviderMetrics::default(),
metrics: StateProviderMetrics::new_with_labels(&[("source", source)]),
total_storage_fetch_latency: AtomicDuration::zero(),
total_code_fetch_latency: AtomicDuration::zero(),
total_account_fetch_latency: AtomicDuration::zero(),
@@ -134,6 +136,12 @@ impl<S> InstrumentedStateProvider<S> {
}
}
impl<S> Drop for InstrumentedStateProvider<S> {
fn drop(&mut self) {
self.record_total_latency();
}
}
/// Metrics for the instrumented state provider
#[derive(Metrics, Clone)]
#[metrics(scope = "sync.state_provider")]

View File

@@ -63,7 +63,7 @@ impl EngineApiMetrics {
pub(crate) fn execute_metered<E, DB>(
&self,
executor: E,
transactions: impl Iterator<Item = Result<impl ExecutableTx<E>, BlockExecutionError>>,
mut transactions: impl Iterator<Item = Result<impl ExecutableTx<E>, BlockExecutionError>>,
state_hook: Box<dyn OnStateHook>,
) -> Result<(BlockExecutionOutput<E::Receipt>, Vec<Address>), BlockExecutionError>
where
@@ -79,27 +79,42 @@ impl EngineApiMetrics {
let mut executor = executor.with_state_hook(Some(Box::new(wrapper)));
let f = || {
let start = Instant::now();
debug_span!(target: "engine::tree", "pre execution")
.entered()
.in_scope(|| executor.apply_pre_execution_changes())?;
self.executor.pre_execution_histogram.record(start.elapsed());
let exec_span = debug_span!(target: "engine::tree", "execution").entered();
for tx in transactions {
loop {
let start = Instant::now();
let Some(tx) = transactions.next() else { break };
self.executor.transaction_wait_histogram.record(start.elapsed());
let tx = tx?;
senders.push(*tx.signer());
let span =
debug_span!(target: "engine::tree", "execute tx", tx_hash=?tx.tx().tx_hash());
let enter = span.entered();
trace!(target: "engine::tree", "Executing transaction");
senders.push(*tx.signer());
let start = Instant::now();
let gas_used = executor.execute_transaction(tx)?;
self.executor.transaction_execution_histogram.record(start.elapsed());
// record the tx gas used
enter.record("gas_used", gas_used);
}
drop(exec_span);
debug_span!(target: "engine::tree", "finish")
let start = Instant::now();
let result = debug_span!(target: "engine::tree", "finish")
.entered()
.in_scope(|| executor.finish())
.map(|(evm, result)| (evm.into_db(), result))
.map(|(evm, result)| (evm.into_db(), result));
self.executor.post_execution_histogram.record(start.elapsed());
result
};
// Use metered to execute and track timing/gas metrics

View File

@@ -54,7 +54,7 @@ use tracing::*;
mod block_buffer;
mod cached_state;
pub mod error;
mod instrumented_state;
pub mod instrumented_state;
mod invalid_headers;
mod metrics;
mod payload_processor;
@@ -1901,6 +1901,16 @@ where
false
}
/// Returns true if the given hash is part of the last received sync target fork choice update.
///
/// See [`ForkchoiceStateTracker::sync_target_state`]
fn is_any_sync_target(&self, block_hash: B256) -> bool {
if let Some(target) = self.state.forkchoice_state_tracker.sync_target_state() {
return target.contains(block_hash)
}
false
}
/// Checks if the given `check` hash points to an invalid header, inserting the given `head`
/// block into the invalid header cache if the `check` hash has a known invalid ancestor.
///
@@ -2040,9 +2050,12 @@ where
match self.insert_block(child) {
Ok(res) => {
debug!(target: "engine::tree", child =?child_num_hash, ?res, "connected buffered block");
if self.is_sync_target_head(child_num_hash.hash) &&
if self.is_any_sync_target(child_num_hash.hash) &&
matches!(res, InsertPayloadOk::Inserted(BlockStatus::Valid))
{
debug!(target: "engine::tree", child =?child_num_hash, "connected sync target block");
// we just inserted a block that we know is part of the canonical chain, so
// we can make it canonical
self.make_canonical(child_num_hash.hash)?;
}
}
@@ -2348,11 +2361,15 @@ where
// try to append the block
match self.insert_block(block) {
Ok(InsertPayloadOk::Inserted(BlockStatus::Valid)) => {
if self.is_sync_target_head(block_num_hash.hash) {
trace!(target: "engine::tree", "appended downloaded sync target block");
// check if we just inserted a block that's part of sync targets,
// i.e. head, safe, or finalized
if let Some(sync_target) = self.state.forkchoice_state_tracker.sync_target_state() &&
sync_target.contains(block_num_hash.hash)
{
debug!(target: "engine::tree", ?sync_target, "appended downloaded sync target block");
// we just inserted the current sync target block, we can try to make it
// canonical
// we just inserted a block that we know is part of the canonical chain, so we
// can make it canonical
return Ok(Some(TreeEvent::TreeAction(TreeAction::MakeCanonical {
sync_target_head: block_num_hash.hash,
})))

View File

@@ -21,6 +21,7 @@ use executor::WorkloadExecutor;
use multiproof::{SparseTrieUpdate, *};
use parking_lot::RwLock;
use prewarm::PrewarmMetrics;
use rayon::iter::{ParallelBridge, ParallelIterator};
use reth_engine_primitives::ExecutableTxIterator;
use reth_evm::{
execute::{ExecutableTxFor, WithTxEnv},
@@ -40,6 +41,7 @@ use reth_trie_sparse::{
};
use reth_trie_sparse_parallel::{ParallelSparseTrie, ParallelismThresholds};
use std::{
collections::BTreeMap,
sync::{
atomic::AtomicBool,
mpsc::{self, channel},
@@ -104,6 +106,8 @@ where
cross_block_cache_size: u64,
/// Whether transactions should not be executed on prewarming task.
disable_transaction_prewarming: bool,
/// Whether state cache should be disable
disable_state_cache: bool,
/// Determines how to configure the evm for execution.
evm_config: Evm,
/// Whether precompile cache should be disabled.
@@ -147,6 +151,7 @@ where
cross_block_cache_size: config.cross_block_cache_size(),
disable_transaction_prewarming: config.disable_prewarming(),
evm_config,
disable_state_cache: config.disable_state_cache(),
precompile_cache_disabled: config.precompile_cache_disabled(),
precompile_cache_map,
sparse_state_trie: Arc::default(),
@@ -312,21 +317,50 @@ where
mpsc::Receiver<Result<WithTxEnv<TxEnvFor<Evm>, I::Tx>, I::Error>>,
usize,
) {
let (transactions, convert) = transactions.into();
let transactions = transactions.into_iter();
// Get the transaction count for prewarming task
// Use upper bound if available (more accurate), otherwise use lower bound
let (lower, upper) = transactions.size_hint();
let transaction_count_hint = upper.unwrap_or(lower);
// Spawn a task that iterates through all transactions in parallel and sends them to the
// main task.
let (tx, rx) = mpsc::channel();
self.executor.spawn_blocking(move || {
transactions.enumerate().par_bridge().for_each_with(tx, |sender, (idx, tx)| {
let tx = convert(tx);
let tx = tx.map(|tx| WithTxEnv { tx_env: tx.to_tx_env(), tx: Arc::new(tx) });
let _ = sender.send((idx, tx));
});
});
// Spawn a task that processes out-of-order transactions from the task above and sends them
// to prewarming and execution tasks.
let (prewarm_tx, prewarm_rx) = mpsc::channel();
let (execute_tx, execute_rx) = mpsc::channel();
self.executor.spawn_blocking(move || {
for tx in transactions {
let tx = tx.map(|tx| WithTxEnv { tx_env: tx.to_tx_env(), tx: Arc::new(tx) });
let mut next_for_execution = 0;
let mut queue = BTreeMap::new();
while let Ok((idx, tx)) = rx.recv() {
// only send Ok(_) variants to prewarming task
if let Ok(tx) = &tx {
let _ = prewarm_tx.send(tx.clone());
}
let _ = execute_tx.send(tx);
if next_for_execution == idx {
let _ = execute_tx.send(tx);
next_for_execution += 1;
while let Some(entry) = queue.first_entry() &&
*entry.key() == next_for_execution
{
let _ = execute_tx.send(entry.remove());
next_for_execution += 1;
}
} else {
queue.insert(idx, tx);
}
}
});
@@ -351,9 +385,15 @@ where
transactions = mpsc::channel().1;
}
let saved_cache = self.cache_for(env.parent_hash);
let cache = saved_cache.cache().clone();
let cache_metrics = saved_cache.metrics().clone();
let (saved_cache, cache, cache_metrics) = if self.disable_state_cache {
(None, None, None)
} else {
let saved_cache = self.cache_for(env.parent_hash);
let cache = saved_cache.cache().clone();
let cache_metrics = saved_cache.metrics().clone();
(Some(saved_cache), Some(cache), Some(cache_metrics))
};
// configure prewarming
let prewarm_ctx = PrewarmContext {
env,
@@ -565,12 +605,12 @@ impl<Tx, Err> PayloadHandle<Tx, Err> {
}
/// Returns a clone of the caches used by prewarming
pub(super) fn caches(&self) -> StateExecutionCache {
pub(super) fn caches(&self) -> Option<StateExecutionCache> {
self.prewarm_handle.cache.clone()
}
/// Returns a clone of the cache metrics used by prewarming
pub(super) fn cache_metrics(&self) -> CachedStateMetrics {
pub(super) fn cache_metrics(&self) -> Option<CachedStateMetrics> {
self.prewarm_handle.cache_metrics.clone()
}
@@ -600,9 +640,9 @@ impl<Tx, Err> PayloadHandle<Tx, Err> {
#[derive(Debug)]
pub(crate) struct CacheTaskHandle {
/// The shared cache the task operates with.
cache: StateExecutionCache,
cache: Option<StateExecutionCache>,
/// Metrics for the caches
cache_metrics: CachedStateMetrics,
cache_metrics: Option<CachedStateMetrics>,
/// Channel to the spawned prewarm task if any
to_prewarm_task: Option<std::sync::mpsc::Sender<PrewarmTaskEvent>>,
}
@@ -1017,13 +1057,19 @@ mod tests {
let provider_factory = BlockchainProvider::new(factory).unwrap();
let mut handle = payload_processor.spawn(
Default::default(),
core::iter::empty::<Result<Recovered<TransactionSigned>, core::convert::Infallible>>(),
StateProviderBuilder::new(provider_factory.clone(), genesis_hash, None),
OverlayStateProviderFactory::new(provider_factory),
&TreeConfig::default(),
);
let mut handle =
payload_processor.spawn(
Default::default(),
(
core::iter::empty::<
Result<Recovered<TransactionSigned>, core::convert::Infallible>,
>(),
std::convert::identity,
),
StateProviderBuilder::new(provider_factory.clone(), genesis_hash, None),
OverlayStateProviderFactory::new(provider_factory),
&TreeConfig::default(),
);
let mut state_hook = handle.state_hook();

File diff suppressed because it is too large Load Diff

View File

@@ -29,7 +29,7 @@ use metrics::{Counter, Gauge, Histogram};
use reth_evm::{execute::ExecutableTxFor, ConfigureEvm, Evm, EvmFor, SpecFor};
use reth_metrics::Metrics;
use reth_primitives_traits::NodePrimitives;
use reth_provider::{BlockReader, StateProviderFactory, StateReader};
use reth_provider::{BlockReader, StateProviderBox, StateProviderFactory, StateReader};
use reth_revm::{database::StateProviderDatabase, db::BundleState, state::EvmState};
use reth_trie::MultiProofTargets;
use std::{
@@ -255,31 +255,35 @@ where
self;
let hash = env.hash;
debug!(target: "engine::caching", parent_hash=?hash, "Updating execution cache");
// Perform all cache operations atomically under the lock
execution_cache.update_with_guard(|cached| {
// consumes the `SavedCache` held by the prewarming task, which releases its usage guard
let (caches, cache_metrics) = saved_cache.split();
let new_cache = SavedCache::new(hash, caches, cache_metrics);
if let Some(saved_cache) = saved_cache {
debug!(target: "engine::caching", parent_hash=?hash, "Updating execution cache");
// Perform all cache operations atomically under the lock
execution_cache.update_with_guard(|cached| {
// consumes the `SavedCache` held by the prewarming task, which releases its usage
// guard
let (caches, cache_metrics) = saved_cache.split();
let new_cache = SavedCache::new(hash, caches, cache_metrics);
// Insert state into cache while holding the lock
if new_cache.cache().insert_state(&state).is_err() {
// Clear the cache on error to prevent having a polluted cache
*cached = None;
debug!(target: "engine::caching", "cleared execution cache on update error");
return;
}
// Insert state into cache while holding the lock
if new_cache.cache().insert_state(&state).is_err() {
// Clear the cache on error to prevent having a polluted cache
*cached = None;
debug!(target: "engine::caching", "cleared execution cache on update error");
return;
}
new_cache.update_metrics();
new_cache.update_metrics();
// Replace the shared cache with the new one; the previous cache (if any) is dropped.
*cached = Some(new_cache);
});
// Replace the shared cache with the new one; the previous cache (if any) is
// dropped.
*cached = Some(new_cache);
});
let elapsed = start.elapsed();
debug!(target: "engine::caching", parent_hash=?hash, elapsed=?elapsed, "Updated execution cache");
let elapsed = start.elapsed();
debug!(target: "engine::caching", parent_hash=?hash, elapsed=?elapsed, "Updated execution cache");
metrics.cache_saving_duration.set(elapsed.as_secs_f64());
metrics.cache_saving_duration.set(elapsed.as_secs_f64());
}
}
/// Executes the task.
@@ -356,7 +360,7 @@ where
{
pub(super) env: ExecutionEnv<Evm>,
pub(super) evm_config: Evm,
pub(super) saved_cache: SavedCache,
pub(super) saved_cache: Option<SavedCache>,
/// Provider to obtain the state
pub(super) provider: StateProviderBuilder<N, P>,
pub(super) metrics: PrewarmMetrics,
@@ -400,10 +404,13 @@ where
};
// Use the caches to create a new provider with caching
let caches = saved_cache.cache().clone();
let cache_metrics = saved_cache.metrics().clone();
let state_provider =
CachedStateProvider::new_with_caches(state_provider, caches, cache_metrics);
let state_provider: StateProviderBox = if let Some(saved_cache) = saved_cache {
let caches = saved_cache.cache().clone();
let cache_metrics = saved_cache.metrics().clone();
Box::new(CachedStateProvider::new_with_caches(state_provider, caches, cache_metrics))
} else {
state_provider
};
let state_provider = StateProviderDatabase::new(state_provider);

View File

@@ -11,6 +11,7 @@ use crate::tree::{
StateProviderDatabase, TreeConfig,
};
use alloy_consensus::transaction::Either;
use alloy_eip7928::BlockAccessList;
use alloy_eips::{eip1898::BlockWithParent, NumHash};
use alloy_evm::Evm;
use alloy_primitives::B256;
@@ -213,16 +214,31 @@ where
Evm: ConfigureEngineEvm<T::ExecutionData, Primitives = N>,
{
match input {
BlockOrPayload::Payload(payload) => Ok(Either::Left(
self.evm_config
BlockOrPayload::Payload(payload) => {
let (iter, convert) = self
.evm_config
.tx_iterator_for_payload(payload)
.map_err(NewPayloadError::other)?
.map(|res| res.map(Either::Left).map_err(NewPayloadError::other)),
)),
.into();
let iter = Either::Left(iter.into_iter().map(Either::Left));
let convert = move |tx| {
let Either::Left(tx) = tx else { unreachable!() };
convert(tx).map(Either::Left).map_err(Either::Left)
};
// Box the closure to satisfy the `Fn` bound both here and in the branch below
Ok((iter, Box::new(convert) as Box<dyn Fn(_) -> _ + Send + Sync + 'static>))
}
BlockOrPayload::Block(block) => {
Ok(Either::Right(block.body().clone_transactions().into_iter().map(|tx| {
Ok(Either::Right(tx.try_into_recovered().map_err(NewPayloadError::other)?))
})))
let iter =
Either::Right(block.body().clone_transactions().into_iter().map(Either::Right));
let convert = move |tx: Either<_, N::SignedTx>| {
let Either::Right(tx) = tx else { unreachable!() };
tx.try_into_recovered().map(Either::Right).map_err(Either::Right)
};
Ok((iter, Box::new(convert)))
}
}
}
@@ -353,7 +369,7 @@ where
)
.into())
};
let state_provider = ensure_ok!(provider_builder.build());
let mut state_provider = ensure_ok!(provider_builder.build());
drop(_enter);
// fetch parent block
@@ -396,18 +412,19 @@ where
// Use cached state provider before executing, used in execution after prewarming threads
// complete
let state_provider = CachedStateProvider::new_with_caches(
state_provider,
handle.caches(),
handle.cache_metrics(),
);
if let Some((caches, cache_metrics)) = handle.caches().zip(handle.cache_metrics()) {
state_provider = Box::new(CachedStateProvider::new_with_caches(
state_provider,
caches,
cache_metrics,
));
};
// Execute the block and handle any execution errors
let (output, senders) = match if self.config.state_provider_metrics() {
let state_provider = InstrumentedStateProvider::from_state_provider(&state_provider);
let result = self.execute_block(&state_provider, env, &input, &mut handle);
state_provider.record_total_latency();
result
let state_provider =
InstrumentedStateProvider::from_state_provider(&state_provider, "engine");
self.execute_block(&state_provider, env, &input, &mut handle)
} else {
self.execute_block(&state_provider, env, &input, &mut handle)
} {
@@ -1228,4 +1245,10 @@ impl<T: PayloadTypes> BlockOrPayload<T> {
Self::Block(_) => "block",
}
}
/// Returns the block access list if available.
pub const fn block_access_list(&self) -> Option<Result<BlockAccessList, alloy_rlp::Error>> {
// TODO decode and return `BlockAccessList`
None
}
}

View File

@@ -252,7 +252,7 @@ where
/// Extracts block headers and bodies from `iter` and appends them using `writer` and `provider`.
///
/// Adds on to `total_difficulty` and collects hash to height using `hash_collector`.
/// Collects hash to height using `hash_collector`.
///
/// Skips all blocks below the [`start_bound`] of `block_numbers` and stops when reaching past the
/// [`end_bound`] or the end of the file.

View File

@@ -1,190 +0,0 @@
//! Simple decoding and decompressing tests
//! for mainnet era files
use reth_era::{
common::file_ops::{StreamReader, StreamWriter},
era::file::{EraReader, EraWriter},
};
use std::io::Cursor;
use crate::{EraTestDownloader, HOODI};
// Helper function to test decompression and decoding for a specific era file
async fn test_era_file_decompression_and_decoding(
downloader: &EraTestDownloader,
filename: &str,
network: &str,
) -> eyre::Result<()> {
println!("\nTesting file: {filename}");
let file = downloader.open_era_file(filename, network).await?;
// Handle genesis era separately
if file.group.is_genesis() {
// Genesis has no blocks
assert_eq!(file.group.blocks.len(), 0, "Genesis should have no blocks");
assert!(file.group.slot_index.is_none(), "Genesis should not have block slot index");
// Test genesis state decompression
let state_data = file.group.era_state.decompress()?;
assert!(!state_data.is_empty(), "Genesis state should decompress to non-empty data");
// Verify state slot index
assert_eq!(
file.group.state_slot_index.slot_count(),
1,
"Genesis state index should have count of 1"
);
let mut buffer = Vec::new();
{
let mut writer = EraWriter::new(&mut buffer);
writer.write_file(&file)?;
}
let reader = EraReader::new(Cursor::new(&buffer));
let read_back_file = reader.read(file.id.network_name.clone())?;
assert_eq!(
file.group.era_state.decompress()?,
read_back_file.group.era_state.decompress()?,
"Genesis state data should be identical"
);
println!("Genesis era verified successfully");
return Ok(());
}
// Non-genesis era - test beacon blocks
println!(
" Non-genesis era with {} beacon blocks, starting at slot {}",
file.group.blocks.len(),
file.group.starting_slot()
);
// Test beacon block decompression across different positions
let test_block_indices = [
0, // First block
file.group.blocks.len() / 2, // Middle block
file.group.blocks.len() - 1, // Last block
];
for &block_idx in &test_block_indices {
let block = &file.group.blocks[block_idx];
let slot = file.group.starting_slot() + block_idx as u64;
println!(
"\n Testing beacon block at slot {}, compressed size: {} bytes",
slot,
block.data.len()
);
// Test beacon block decompression
let block_data = block.decompress()?;
assert!(
!block_data.is_empty(),
"Beacon block at slot {slot} decompression should produce non-empty data"
);
}
// Test era state decompression
let state_data = file.group.era_state.decompress()?;
assert!(!state_data.is_empty(), "Era state decompression should produce non-empty data");
println!(" Era state decompressed: {} bytes", state_data.len());
// Verify slot indices
if let Some(ref block_slot_index) = file.group.slot_index {
println!(
" Block slot index: starting_slot={}, count={}",
block_slot_index.starting_slot,
block_slot_index.slot_count()
);
// Check for empty slots
let empty_slots: Vec<usize> = (0..block_slot_index.slot_count())
.filter(|&i| !block_slot_index.has_data_at_slot(i))
.collect();
if !empty_slots.is_empty() {
println!(
" Found {} empty slots (first few): {:?}",
empty_slots.len(),
&empty_slots[..empty_slots.len().min(5)]
);
}
}
// Test round-trip serialization
let mut buffer = Vec::new();
{
let mut writer = EraWriter::new(&mut buffer);
writer.write_file(&file)?;
}
// Read back from buffer
let reader = EraReader::new(Cursor::new(&buffer));
let read_back_file = reader.read(file.id.network_name.clone())?;
// Verify basic properties are preserved
assert_eq!(file.id.network_name, read_back_file.id.network_name);
assert_eq!(file.id.start_slot, read_back_file.id.start_slot);
assert_eq!(file.id.slot_count, read_back_file.id.slot_count);
assert_eq!(file.group.blocks.len(), read_back_file.group.blocks.len());
// Test data preservation for beacon blocks
for &idx in &test_block_indices {
let original_block = &file.group.blocks[idx];
let read_back_block = &read_back_file.group.blocks[idx];
let slot = file.group.starting_slot() + idx as u64;
// Test that decompressed data is identical
assert_eq!(
original_block.decompress()?,
read_back_block.decompress()?,
"Beacon block data should be identical for slot {slot}"
);
}
// Test state data preservation
assert_eq!(
file.group.era_state.decompress()?,
read_back_file.group.era_state.decompress()?,
"Era state data should be identical"
);
// Test slot indices preservation
if let (Some(original_index), Some(read_index)) =
(&file.group.slot_index, &read_back_file.group.slot_index)
{
assert_eq!(
original_index.starting_slot, read_index.starting_slot,
"Block slot index starting slot should match"
);
assert_eq!(
original_index.offsets, read_index.offsets,
"Block slot index offsets should match"
);
}
assert_eq!(
file.group.state_slot_index.starting_slot,
read_back_file.group.state_slot_index.starting_slot,
"State slot index starting slot should match"
);
assert_eq!(
file.group.state_slot_index.offsets, read_back_file.group.state_slot_index.offsets,
"State slot index offsets should match"
);
Ok(())
}
#[test_case::test_case("hoodi-00000-212f13fc.era"; "era_dd_hoodi_0")]
#[test_case::test_case("hoodi-00021-857e418b.era"; "era_dd_hoodi_21")]
#[test_case::test_case("hoodi-00175-202aaa6d.era"; "era_dd_hoodi_175")]
#[test_case::test_case("hoodi-00201-0d521fc8.era"; "era_dd_hoodi_201")]
#[tokio::test(flavor = "multi_thread")]
#[ignore = "download intensive"]
async fn test_hoodi_era1_file_decompression_and_decoding(filename: &str) -> eyre::Result<()> {
let downloader = EraTestDownloader::new().await?;
test_era_file_decompression_and_decoding(&downloader, filename, HOODI).await
}

View File

@@ -1,2 +1,2 @@
mod dd;
mod genesis;
mod roundtrip;

View File

@@ -0,0 +1,228 @@
//! Roundtrip tests for `.era` files.
//!
//! These tests verify the full lifecycle of era files by:
//! - Reading files from their original source
//! - Decompressing their contents
//! - Re-compressing the data
//! - Writing the data back to a new file
//! - Confirming that all original data is preserved throughout the process
//!
//!
//! Only a couple of era files are downloaded from `https://mainnet.era.nimbus.team/` for mainnet
//! and `https://hoodi.era.nimbus.team/` for hoodi to keep the tests efficient.
use reth_era::{
common::file_ops::{EraFileFormat, StreamReader, StreamWriter},
era::{
file::{EraFile, EraReader, EraWriter},
types::{
consensus::{CompressedBeaconState, CompressedSignedBeaconBlock},
group::{EraGroup, EraId},
},
},
};
use std::io::Cursor;
use crate::{EraTestDownloader, HOODI, MAINNET};
// Helper function to test roundtrip compression/encoding for a specific file
async fn test_era_file_roundtrip(
downloader: &EraTestDownloader,
filename: &str,
network: &str,
) -> eyre::Result<()> {
println!("\nTesting roundtrip for file: {filename}");
let original_file = downloader.open_era_file(filename, network).await?;
if original_file.group.is_genesis() {
println!("Genesis era detected, using special handling");
assert_eq!(original_file.group.blocks.len(), 0, "Genesis should have no blocks");
assert!(
original_file.group.slot_index.is_none(),
"Genesis should not have block slot index"
);
let state_data = original_file.group.era_state.decompress()?;
println!(" Genesis state decompressed: {} bytes", state_data.len());
// File roundtrip test
let mut buffer = Vec::new();
{
let mut writer = EraWriter::new(&mut buffer);
writer.write_file(&original_file)?;
}
let reader = EraReader::new(Cursor::new(&buffer));
let roundtrip_file = reader.read(network.to_string())?;
assert_eq!(
original_file.group.era_state.decompress()?,
roundtrip_file.group.era_state.decompress()?,
"Genesis state data should be identical after roundtrip"
);
println!("Genesis era verified successfully");
return Ok(());
}
// non genesis start
let original_state_data = original_file.group.era_state.decompress()?;
let mut buffer = Vec::new();
{
let mut writer = EraWriter::new(&mut buffer);
writer.write_file(&original_file)?;
}
// Read back from buffer
let reader = EraReader::new(Cursor::new(&buffer));
let roundtrip_file = reader.read(network.to_string())?;
assert_eq!(
original_file.id.network_name, roundtrip_file.id.network_name,
"Network name should match after roundtrip"
);
assert_eq!(
original_file.id.start_slot, roundtrip_file.id.start_slot,
"Start slot should match after roundtrip"
);
assert_eq!(
original_file.group.blocks.len(),
roundtrip_file.group.blocks.len(),
"Block count should match after roundtrip"
);
// Select a few blocks to test
let test_block_indices = [
0, // First block
original_file.group.blocks.len() / 2, // Middle block
original_file.group.blocks.len() - 1, // Last block
];
// Test individual beacon blocks
for &block_idx in &test_block_indices {
let original_block = &original_file.group.blocks[block_idx];
let roundtrip_block = &roundtrip_file.group.blocks[block_idx];
let original_block_data = original_block.decompress()?;
let roundtrip_block_data = roundtrip_block.decompress()?;
// Verify file roundtrip preserves data
assert_eq!(
original_block_data, roundtrip_block_data,
"Block {block_idx} data should be identical after file roundtrip"
);
// Verify compression roundtrip
let recompressed_block = CompressedSignedBeaconBlock::from_ssz(&original_block_data)?;
let recompressed_block_data = recompressed_block.decompress()?;
assert_eq!(
original_block_data, recompressed_block_data,
"Block {block_idx} should be identical after re-compression cycle"
);
}
let roundtrip_state_data = roundtrip_file.group.era_state.decompress()?;
assert_eq!(
original_state_data, roundtrip_state_data,
"Era state data should be identical after roundtrip"
);
let recompressed_state = CompressedBeaconState::from_ssz(&roundtrip_state_data)?;
let recompressed_state_data = recompressed_state.decompress()?;
assert_eq!(
original_state_data, recompressed_state_data,
"Era state data should be identical after re-compression cycle"
);
let recompressed_blocks: Vec<CompressedSignedBeaconBlock> = roundtrip_file
.group
.blocks
.iter()
.map(|block| {
let data = block.decompress()?;
CompressedSignedBeaconBlock::from_ssz(&data)
})
.collect::<Result<Vec<_>, _>>()?;
let new_group = if let Some(ref block_index) = roundtrip_file.group.slot_index {
EraGroup::with_block_index(
recompressed_blocks,
recompressed_state,
block_index.clone(),
roundtrip_file.group.state_slot_index.clone(),
)
} else {
EraGroup::new(
recompressed_blocks,
recompressed_state,
roundtrip_file.group.state_slot_index,
)
};
let (start_slot, slot_count) = new_group.slot_range();
let new_file = EraFile::new(new_group, EraId::new(network, start_slot, slot_count));
let mut reconstructed_buffer = Vec::new();
{
let mut writer = EraWriter::new(&mut reconstructed_buffer);
writer.write_file(&new_file)?;
}
let reader = EraReader::new(Cursor::new(&reconstructed_buffer));
let reconstructed_file = reader.read(network.to_string())?;
assert_eq!(
original_file.group.blocks.len(),
reconstructed_file.group.blocks.len(),
"Block count should match after full reconstruction"
);
// Verify all reconstructed blocks match
for (idx, (orig, recon)) in
original_file.group.blocks.iter().zip(reconstructed_file.group.blocks.iter()).enumerate()
{
assert_eq!(
orig.decompress()?,
recon.decompress()?,
"Block {idx} should match after full reconstruction"
);
}
// Verify reconstructed state matches
assert_eq!(
original_state_data,
reconstructed_file.group.era_state.decompress()?,
"State should match after full reconstruction"
);
println!("File {filename} roundtrip successful");
Ok(())
}
#[test_case::test_case("mainnet-00000-4b363db9.era"; "era_roundtrip_mainnet_0")]
#[test_case::test_case("mainnet-00178-0d0a5290.era"; "era_roundtrip_mainnet_178")]
#[test_case::test_case("mainnet-01070-7616e3e2.era"; "era_roundtrip_mainnet_1070")]
#[test_case::test_case("mainnet-01267-e3ddc749.era"; "era_roundtrip_mainnet_1267")]
#[test_case::test_case("mainnet-01592-d4dc8b98.era"; "era_roundtrip_mainnet_1592")]
#[tokio::test(flavor = "multi_thread")]
#[ignore = "download intensive"]
async fn test_roundtrip_compression_encoding_mainnet(filename: &str) -> eyre::Result<()> {
let downloader = EraTestDownloader::new().await?;
test_era_file_roundtrip(&downloader, filename, MAINNET).await
}
#[test_case::test_case("hoodi-00000-212f13fc.era"; "era_roundtrip_hoodi_0")]
#[test_case::test_case("hoodi-00021-857e418b.era"; "era_roundtrip_hoodi_21")]
#[test_case::test_case("hoodi-00175-202aaa6d.era"; "era_roundtrip_hoodi_175")]
#[test_case::test_case("hoodi-00201-0d521fc8.era"; "era_roundtrip_hoodi_201")]
#[tokio::test(flavor = "multi_thread")]
#[ignore = "download intensive"]
async fn test_roundtrip_compression_encoding_hoodi(filename: &str) -> eyre::Result<()> {
let downloader = EraTestDownloader::new().await?;
test_era_file_roundtrip(&downloader, filename, HOODI).await
}

View File

@@ -1,159 +0,0 @@
//! Simple decoding and decompressing tests
//! for mainnet era1 files
use alloy_consensus::{BlockBody, Header};
use alloy_primitives::U256;
use reth_era::{
common::file_ops::{StreamReader, StreamWriter},
e2s::types::IndexEntry,
era1::{
file::{Era1Reader, Era1Writer},
types::execution::CompressedBody,
},
};
use reth_ethereum_primitives::TransactionSigned;
use std::io::Cursor;
use crate::{EraTestDownloader, MAINNET};
// Helper function to test decompression and decoding for a specific era1 file
async fn test_file_decompression(
downloader: &EraTestDownloader,
filename: &str,
) -> eyre::Result<()> {
println!("\nTesting file: {filename}");
let file = downloader.open_era1_file(filename, MAINNET).await?;
// Test block decompression across different positions in the file
let test_block_indices = [
0, // First block
file.group.blocks.len() / 2, // Middle block
file.group.blocks.len() - 1, // Last block
];
for &block_idx in &test_block_indices {
let block = &file.group.blocks[block_idx];
let block_number = file.group.block_index.starting_number() + block_idx as u64;
println!(
"\n Testing block {}, compressed body size: {} bytes",
block_number,
block.body.data.len()
);
// Test header decompression and decoding
let header_data = block.header.decompress()?;
assert!(
!header_data.is_empty(),
"Block {block_number} header decompression should produce non-empty data"
);
let header = block.header.decode_header()?;
assert_eq!(header.number, block_number, "Decoded header should have correct block number");
println!("Header decompression and decoding successful");
// Test body decompression
let body_data = block.body.decompress()?;
assert!(
!body_data.is_empty(),
"Block {block_number} body decompression should produce non-empty data"
);
println!("Body decompression successful ({} bytes)", body_data.len());
let decoded_body: BlockBody<TransactionSigned> =
CompressedBody::decode_body_from_decompressed::<TransactionSigned, Header>(&body_data)
.expect("Failed to decode body");
println!(
"Body decoding successful: {} transactions, {} ommers, withdrawals: {}",
decoded_body.transactions.len(),
decoded_body.ommers.len(),
decoded_body.withdrawals.is_some()
);
// Test receipts decompression
let receipts_data = block.receipts.decompress()?;
assert!(
!receipts_data.is_empty(),
"Block {block_number} receipts decompression should produce non-empty data"
);
println!("Receipts decompression successful ({} bytes)", receipts_data.len());
assert!(
block.total_difficulty.value > U256::ZERO,
"Block {block_number} should have non-zero difficulty"
);
println!("Total difficulty verified: {}", block.total_difficulty.value);
}
// Test round-trip serialization
println!("\n Testing data preservation roundtrip...");
let mut buffer = Vec::new();
{
let mut writer = Era1Writer::new(&mut buffer);
writer.write_file(&file)?;
}
// Read back from buffer
let reader = Era1Reader::new(Cursor::new(&buffer));
let read_back_file = reader.read(file.id.network_name.clone())?;
// Verify basic properties are preserved
assert_eq!(file.id.network_name, read_back_file.id.network_name);
assert_eq!(file.id.start_block, read_back_file.id.start_block);
assert_eq!(file.group.blocks.len(), read_back_file.group.blocks.len());
assert_eq!(file.group.accumulator.root, read_back_file.group.accumulator.root);
// Test data preservation for some blocks
for &idx in &test_block_indices {
let original_block = &file.group.blocks[idx];
let read_back_block = &read_back_file.group.blocks[idx];
let block_number = file.group.block_index.starting_number() + idx as u64;
println!("Block {block_number} details:");
println!(" Header size: {} bytes", original_block.header.data.len());
println!(" Body size: {} bytes", original_block.body.data.len());
println!(" Receipts size: {} bytes", original_block.receipts.data.len());
// Test that decompressed data is identical
assert_eq!(
original_block.header.decompress()?,
read_back_block.header.decompress()?,
"Header data should be identical for block {block_number}"
);
assert_eq!(
original_block.body.decompress()?,
read_back_block.body.decompress()?,
"Body data should be identical for block {block_number}"
);
assert_eq!(
original_block.receipts.decompress()?,
read_back_block.receipts.decompress()?,
"Receipts data should be identical for block {block_number}"
);
assert_eq!(
original_block.total_difficulty.value, read_back_block.total_difficulty.value,
"Total difficulty should be identical for block {block_number}"
);
}
Ok(())
}
#[test_case::test_case("mainnet-00000-5ec1ffb8.era1"; "era_dd_mainnet_0")]
#[test_case::test_case("mainnet-00003-d8b8a40b.era1"; "era_dd_mainnet_3")]
#[test_case::test_case("mainnet-00151-e322efe1.era1"; "era_dd_mainnet_151")]
#[test_case::test_case("mainnet-00293-0d6c5812.era1"; "era_dd_mainnet_293")]
#[test_case::test_case("mainnet-00443-ea71b6f9.era1"; "era_dd_mainnet_443")]
#[test_case::test_case("mainnet-01367-d7efc68f.era1"; "era_dd_mainnet_1367")]
#[test_case::test_case("mainnet-01610-99fdde4b.era1"; "era_dd_mainnet_1610")]
#[test_case::test_case("mainnet-01895-3f81607c.era1"; "era_dd_mainnet_1895")]
#[tokio::test(flavor = "multi_thread")]
#[ignore = "download intensive"]
async fn test_mainnet_era1_file_decompression_and_decoding(filename: &str) -> eyre::Result<()> {
let downloader = EraTestDownloader::new().await?;
test_file_decompression(&downloader, filename).await
}

View File

@@ -1,3 +1,2 @@
mod dd;
mod genesis;
mod roundtrip;

View File

@@ -6,6 +6,9 @@
//! - Re-encoding and recompressing the data
//! - Writing the data back to a new file
//! - Confirming that all original data is preserved throughout the process
//!
//! Only a couple of era1 files are downloaded from <https://era.ithaca.xyz/era1/> for mainnet
//! and <https://era.ithaca.xyz/sepolia-era1/> for sepolia to keep the tests efficient.
use alloy_consensus::{BlockBody, BlockHeader, Header, ReceiptEnvelope};
use reth_era::{
@@ -27,7 +30,7 @@ use std::io::Cursor;
use crate::{EraTestDownloader, MAINNET, SEPOLIA};
// Helper function to test roundtrip compression/encoding for a specific file
async fn test_file_roundtrip(
async fn test_era1_file_roundtrip(
downloader: &EraTestDownloader,
filename: &str,
network: &str,
@@ -252,27 +255,27 @@ async fn test_file_roundtrip(
Ok(())
}
#[test_case::test_case("mainnet-00000-5ec1ffb8.era1"; "era_mainnet_0")]
#[test_case::test_case("mainnet-00151-e322efe1.era1"; "era_mainnet_151")]
#[test_case::test_case("mainnet-01367-d7efc68f.era1"; "era_mainnet_1367")]
#[test_case::test_case("mainnet-01895-3f81607c.era1"; "era_mainnet_1895")]
#[test_case::test_case("mainnet-00000-5ec1ffb8.era1"; "era1_roundtrip_mainnet_0")]
#[test_case::test_case("mainnet-00151-e322efe1.era1"; "era1_roundtrip_mainnet_151")]
#[test_case::test_case("mainnet-01367-d7efc68f.era1"; "era1_roundtrip_mainnet_1367")]
#[test_case::test_case("mainnet-01895-3f81607c.era1"; "era1_roundtrip_mainnet_1895")]
#[tokio::test(flavor = "multi_thread")]
#[ignore = "download intensive"]
async fn test_roundtrip_compression_encoding_mainnet(filename: &str) -> eyre::Result<()> {
let downloader = EraTestDownloader::new().await?;
test_file_roundtrip(&downloader, filename, MAINNET).await
test_era1_file_roundtrip(&downloader, filename, MAINNET).await
}
#[test_case::test_case("sepolia-00000-643a00f7.era1"; "era_sepolia_0")]
#[test_case::test_case("sepolia-00074-0e81003c.era1"; "era_sepolia_74")]
#[test_case::test_case("sepolia-00173-b6924da5.era1"; "era_sepolia_173")]
#[test_case::test_case("sepolia-00182-a4f0a8a1.era1"; "era_sepolia_182")]
#[test_case::test_case("sepolia-00000-643a00f7.era1"; "era1_roundtrip_sepolia_0")]
#[test_case::test_case("sepolia-00074-0e81003c.era1"; "era1_roundtrip_sepolia_74")]
#[test_case::test_case("sepolia-00173-b6924da5.era1"; "era1_roundtrip_sepolia_173")]
#[test_case::test_case("sepolia-00182-a4f0a8a1.era1"; "era1_roundtrip_sepolia_182")]
#[tokio::test(flavor = "multi_thread")]
#[ignore = "download intensive"]
async fn test_roundtrip_compression_encoding_sepolia(filename: &str) -> eyre::Result<()> {
let downloader = EraTestDownloader::new().await?;
test_file_roundtrip(&downloader, filename, SEPOLIA).await?;
test_era1_file_roundtrip(&downloader, filename, SEPOLIA).await?;
Ok(())
}

View File

@@ -91,16 +91,19 @@ const ERA_MAINNET_URL: &str = "https://mainnet.era.nimbus.team/";
/// Succinct list of mainnet files we want to download
/// from <https://mainnet.era.nimbus.team/> //TODO: to replace with internal era files hosting url
/// for testing purposes
const ERA_MAINNET_FILES_NAMES: [&str; 4] = [
const ERA_MAINNET_FILES_NAMES: [&str; 8] = [
"mainnet-00000-4b363db9.era",
"mainnet-00178-0d0a5290.era",
"mainnet-00518-4e267a3a.era",
"mainnet-01140-f70d4869.era",
"mainnet-00780-bb546fec.era",
"mainnet-01070-7616e3e2.era",
"mainnet-01267-e3ddc749.era",
"mainnet-01581-82073d28.era",
"mainnet-01592-d4dc8b98.era",
];
/// Utility for downloading `.era1` files for tests
/// in a temporary directory
/// and caching them in memory
/// Utility for downloading `.era` and `.era1` files for tests
/// in a temporary directory and caching them in memory
#[derive(Debug)]
struct EraTestDownloader {
/// Temporary directory for storing downloaded files
@@ -180,7 +183,7 @@ impl EraTestDownloader {
Ok(())
}
/// Get network configuration, URL and supported files, based on network and file type
/// Get network configuration, URL and supported files, based on network and file type
fn get_network_config(
&self,
filename: &str,
@@ -202,14 +205,13 @@ impl EraTestDownloader {
}
}
/// open .era1 file, downloading it if necessary
/// Open `.era1` file, downloading it if necessary
async fn open_era1_file(&self, filename: &str, network: &str) -> Result<Era1File> {
let path = self.download_file(filename, network).await?;
Era1Reader::open(&path, network).map_err(|e| eyre!("Failed to open Era1 file: {e}"))
}
/// open .era file, downloading it if necessary
#[allow(dead_code)]
/// Open `.era` file, downloading it if necessary
async fn open_era_file(&self, filename: &str, network: &str) -> Result<EraFile> {
let path = self.download_file(filename, network).await?;
EraReader::open(&path, network).map_err(|e| eyre!("Failed to open Era1 file: {e}"))

View File

@@ -5,7 +5,6 @@ use alloy_consensus::{
};
use alloy_eips::merge::BEACON_NONCE;
use alloy_evm::{block::BlockExecutorFactory, eth::EthBlockExecutionCtx};
use alloy_primitives::Bytes;
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_evm::execute::{BlockAssembler, BlockAssemblerInput, BlockExecutionError};
use reth_execution_types::BlockExecutionResult;
@@ -17,14 +16,12 @@ use revm::context::Block as _;
pub struct EthBlockAssembler<ChainSpec = reth_chainspec::ChainSpec> {
/// The chainspec.
pub chain_spec: Arc<ChainSpec>,
/// Extra data to use for the blocks.
pub extra_data: Bytes,
}
impl<ChainSpec> EthBlockAssembler<ChainSpec> {
/// Creates a new [`EthBlockAssembler`].
pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
Self { chain_spec, extra_data: Default::default() }
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
Self { chain_spec }
}
}
@@ -110,7 +107,7 @@ where
gas_limit: evm_env.block_env.gas_limit(),
difficulty: evm_env.block_env.difficulty(),
gas_used: *gas_used,
extra_data: self.extra_data.clone(),
extra_data: ctx.extra_data,
parent_beacon_block_root: ctx.parent_beacon_block_root,
blob_gas_used: block_blob_gas_used,
excess_blob_gas,

View File

@@ -116,12 +116,6 @@ impl<ChainSpec, EvmFactory> EthEvmConfig<ChainSpec, EvmFactory> {
pub const fn chain_spec(&self) -> &Arc<ChainSpec> {
self.executor_factory.spec()
}
/// Sets the extra data for the block assembler.
pub fn with_extra_data(mut self, extra_data: Bytes) -> Self {
self.block_assembler.extra_data = extra_data;
self
}
}
impl<ChainSpec, EvmF> ConfigureEvm for EthEvmConfig<ChainSpec, EvmF>
@@ -193,6 +187,7 @@ where
parent_beacon_block_root: block.header().parent_beacon_block_root,
ommers: &block.body().ommers,
withdrawals: block.body().withdrawals.as_ref().map(Cow::Borrowed),
extra_data: block.header().extra_data.clone(),
})
}
@@ -206,6 +201,7 @@ where
parent_beacon_block_root: attributes.parent_beacon_block_root,
ommers: &[],
withdrawals: attributes.withdrawals.map(Cow::Owned),
extra_data: attributes.extra_data,
})
}
}
@@ -282,6 +278,7 @@ where
parent_beacon_block_root: payload.sidecar.parent_beacon_block_root(),
ommers: &[],
withdrawals: payload.payload.withdrawals().map(|w| Cow::Owned(w.clone().into())),
extra_data: payload.payload.as_v1().extra_data.clone(),
})
}
@@ -289,12 +286,15 @@ where
&self,
payload: &ExecutionData,
) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
Ok(payload.payload.transactions().clone().into_iter().map(|tx| {
let txs = payload.payload.transactions().clone().into_iter();
let convert = |tx: Bytes| {
let tx =
TxTy::<Self::Primitives>::decode_2718_exact(tx.as_ref()).map_err(AnyError::new)?;
let signer = tx.try_recover().map_err(AnyError::new)?;
Ok::<_, AnyError>(tx.with_signer(signer))
}))
};
Ok((txs, convert))
}
}

View File

@@ -61,6 +61,8 @@ reth-node-core.workspace = true
reth-e2e-test-utils.workspace = true
reth-tasks.workspace = true
reth-testing-utils.workspace = true
tempfile.workspace = true
jsonrpsee-core.workspace = true
alloy-primitives.workspace = true
alloy-provider.workspace = true

View File

@@ -32,15 +32,15 @@ use reth_node_builder::{
EngineValidatorBuilder, EthApiBuilder, EthApiCtx, Identity, PayloadValidatorBuilder,
RethRpcAddOns, RpcAddOns, RpcHandle,
},
BuilderContext, DebugNode, Node, NodeAdapter, PayloadBuilderConfig,
BuilderContext, DebugNode, Node, NodeAdapter,
};
use reth_payload_primitives::PayloadTypes;
use reth_provider::{providers::ProviderFactoryBuilder, EthStorage};
use reth_rpc::{
eth::core::{EthApiFor, EthRpcConverterFor},
ValidationApi,
TestingApi, ValidationApi,
};
use reth_rpc_api::servers::BlockSubmissionValidationApiServer;
use reth_rpc_api::servers::{BlockSubmissionValidationApiServer, TestingApiServer};
use reth_rpc_builder::{config::RethRpcServerConfig, middleware::RethRpcMiddleware};
use reth_rpc_eth_api::{
helpers::{
@@ -313,6 +313,17 @@ where
.modules
.merge_if_module_configured(RethRpcModule::Eth, eth_config.into_rpc())?;
// testing_buildBlockV1: only wire when the hidden testing module is explicitly
// requested on any transport. Default stays disabled to honor security guidance.
let testing_api = TestingApi::new(
container.registry.eth_api().clone(),
container.registry.evm_config().clone(),
)
.into_rpc();
container
.modules
.merge_if_module_configured(RethRpcModule::Testing, testing_api)?;
Ok(())
})
.await
@@ -426,9 +437,7 @@ where
type EVM = EthEvmConfig<Types::ChainSpec>;
async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
let evm_config = EthEvmConfig::new(ctx.chain_spec())
.with_extra_data(ctx.payload_builder_config().extra_data_bytes());
Ok(evm_config)
Ok(EthEvmConfig::new(ctx.chain_spec()))
}
}

View File

@@ -54,7 +54,8 @@ where
evm_config,
EthereumBuilderConfig::new()
.with_gas_limit(gas_limit)
.with_max_blobs_per_block(conf.max_blobs_per_block()),
.with_max_blobs_per_block(conf.max_blobs_per_block())
.with_extra_data(conf.extra_data_bytes()),
))
}
}

View File

@@ -2,5 +2,6 @@
mod builder;
mod exex;
mod testing;
const fn main() {}

View File

@@ -0,0 +1,84 @@
//! E2E tests for the testing RPC namespace.
use alloy_primitives::{Address, B256};
use alloy_rpc_types_engine::ExecutionPayloadEnvelopeV4;
use jsonrpsee_core::client::ClientT;
use reth_db::test_utils::create_test_rw_db;
use reth_ethereum_engine_primitives::EthPayloadAttributes;
use reth_node_builder::{NodeBuilder, NodeConfig};
use reth_node_core::{
args::DatadirArgs,
dirs::{DataDirPath, MaybePlatformPath},
};
use reth_node_ethereum::{node::EthereumAddOns, EthereumNode};
use reth_rpc_api::TestingBuildBlockRequestV1;
use reth_rpc_server_types::{RethRpcModule, RpcModuleSelection};
use reth_tasks::TaskManager;
use std::str::FromStr;
use tempfile::tempdir;
use tokio::sync::oneshot;
#[tokio::test(flavor = "multi_thread")]
async fn testing_rpc_build_block_works() -> eyre::Result<()> {
let tasks = TaskManager::current();
let mut rpc_args = reth_node_core::args::RpcServerArgs::default().with_http();
rpc_args.http_api = Some(RpcModuleSelection::from_iter([RethRpcModule::Testing]));
let tempdir = tempdir().expect("temp datadir");
let datadir_args = DatadirArgs {
datadir: MaybePlatformPath::<DataDirPath>::from_str(tempdir.path().to_str().unwrap())
.expect("valid datadir"),
static_files_path: Some(tempdir.path().join("static")),
};
let config = NodeConfig::test().with_datadir_args(datadir_args).with_rpc(rpc_args);
let db = create_test_rw_db();
let (tx, rx): (
oneshot::Sender<eyre::Result<ExecutionPayloadEnvelopeV4>>,
oneshot::Receiver<eyre::Result<ExecutionPayloadEnvelopeV4>>,
) = oneshot::channel();
let builder = NodeBuilder::new(config)
.with_database(db)
.with_launch_context(tasks.executor())
.with_types::<EthereumNode>()
.with_components(EthereumNode::components())
.with_add_ons(EthereumAddOns::default())
.on_rpc_started(move |ctx, handles| {
let Some(client) = handles.rpc.http_client() else { return Ok(()) };
let chain = ctx.config().chain.clone();
let parent_block_hash = chain.genesis_hash();
let payload_attributes = EthPayloadAttributes {
timestamp: chain.genesis().timestamp + 1,
prev_randao: B256::ZERO,
suggested_fee_recipient: Address::ZERO,
withdrawals: None,
parent_beacon_block_root: None,
};
let request = TestingBuildBlockRequestV1 {
parent_block_hash,
payload_attributes,
transactions: vec![],
extra_data: None,
};
tokio::spawn(async move {
let res: eyre::Result<ExecutionPayloadEnvelopeV4> =
client.request("testing_buildBlockV1", [request]).await.map_err(Into::into);
let _ = tx.send(res);
});
Ok(())
});
// Launch the node with the default engine launcher.
let launcher = builder.engine_api_launcher();
let _node = builder.launch_with(launcher).await?;
// Wait for the testing RPC call to return.
let res = rx.await.expect("testing_buildBlockV1 response");
assert!(res.is_ok(), "testing_buildBlockV1 failed: {:?}", res.err());
Ok(())
}

View File

@@ -1,4 +1,5 @@
use alloy_eips::eip1559::ETHEREUM_BLOCK_GAS_LIMIT_30M;
use alloy_primitives::Bytes;
use reth_primitives_traits::constants::GAS_LIMIT_BOUND_DIVISOR;
/// Settings for the Ethereum builder.
@@ -13,6 +14,8 @@ pub struct EthereumBuilderConfig {
///
/// If `None`, defaults to the protocol maximum.
pub max_blobs_per_block: Option<u64>,
/// Extra data for built blocks.
pub extra_data: Bytes,
}
impl Default for EthereumBuilderConfig {
@@ -28,6 +31,7 @@ impl EthereumBuilderConfig {
desired_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
await_payload_on_missing: true,
max_blobs_per_block: None,
extra_data: Bytes::new(),
}
}
@@ -49,6 +53,12 @@ impl EthereumBuilderConfig {
self.max_blobs_per_block = max_blobs_per_block;
self
}
/// Set the extra data for built blocks.
pub fn with_extra_data(mut self, extra_data: Bytes) -> Self {
self.extra_data = extra_data;
self
}
}
impl EthereumBuilderConfig {

View File

@@ -168,6 +168,7 @@ where
gas_limit: builder_config.gas_limit(parent_header.gas_limit),
parent_beacon_block_root: attributes.parent_beacon_block_root(),
withdrawals: Some(attributes.withdrawals().clone()),
extra_data: builder_config.extra_data,
},
)
.map_err(PayloadBuilderError::other)?;

View File

@@ -18,22 +18,51 @@ pub trait ConfigureEngineEvm<ExecutionData>: ConfigureEvm {
) -> Result<impl ExecutableTxIterator<Self>, Self::Error>;
}
/// Iterator over executable transactions.
pub trait ExecutableTxIterator<Evm: ConfigureEvm>:
Iterator<Item = Result<Self::Tx, Self::Error>> + Send + 'static
{
/// A helper trait representing a pair of a "raw" transactions iterator and a closure that can be
/// used to convert them to an executable transaction. This tuple is used in the engine to
/// parallelize heavy work like decoding or recovery.
pub trait ExecutableTxTuple: Into<(Self::Iter, Self::Convert)> + Send + 'static {
/// Raw transaction that can be converted to an [`ExecutableTxTuple::Tx`]
///
/// This can be any type that can be converted to an [`ExecutableTxTuple::Tx`]. For example,
/// an unrecovered transaction or just the transaction bytes.
type RawTx: Send + Sync + 'static;
/// The executable transaction type iterator yields.
type Tx: ExecutableTxFor<Evm> + Clone + Send + Sync + 'static;
type Tx: Clone + Send + Sync + 'static;
/// Errors that may occur while recovering or decoding transactions.
type Error: core::error::Error + Send + Sync + 'static;
/// Iterator over [`ExecutableTxTuple::Tx`]
type Iter: Iterator<Item = Self::RawTx> + Send + 'static;
/// Closure that can be used to convert a [`ExecutableTxTuple::RawTx`] to a
/// [`ExecutableTxTuple::Tx`]. This might involve heavy work like decoding or recovery
/// and will be parallelized in the engine.
type Convert: Fn(Self::RawTx) -> Result<Self::Tx, Self::Error> + Send + Sync + 'static;
}
impl<Evm: ConfigureEvm, Tx, Err, T> ExecutableTxIterator<Evm> for T
impl<RawTx, Tx, Err, I, F> ExecutableTxTuple for (I, F)
where
Tx: ExecutableTxFor<Evm> + Clone + Send + Sync + 'static,
RawTx: Send + Sync + 'static,
Tx: Clone + Send + Sync + 'static,
Err: core::error::Error + Send + Sync + 'static,
T: Iterator<Item = Result<Tx, Err>> + Send + 'static,
I: Iterator<Item = RawTx> + Send + 'static,
F: Fn(RawTx) -> Result<Tx, Err> + Send + Sync + 'static,
{
type RawTx = RawTx;
type Tx = Tx;
type Error = Err;
type Iter = I;
type Convert = F;
}
/// Iterator over executable transactions.
pub trait ExecutableTxIterator<Evm: ConfigureEvm>:
ExecutableTxTuple<Tx: ExecutableTxFor<Evm>>
{
}
impl<T, Evm: ConfigureEvm> ExecutableTxIterator<Evm> for T where
T: ExecutableTxTuple<Tx: ExecutableTxFor<Evm>>
{
}

View File

@@ -28,7 +28,7 @@ use alloy_evm::{
block::{BlockExecutorFactory, BlockExecutorFor},
precompiles::PrecompilesMap,
};
use alloy_primitives::{Address, B256};
use alloy_primitives::{Address, Bytes, B256};
use core::{error::Error, fmt::Debug};
use execute::{BasicBlockExecutor, BlockAssembler, BlockBuilder};
use reth_execution_errors::BlockExecutionError;
@@ -501,6 +501,8 @@ pub struct NextBlockEnvAttributes {
pub parent_beacon_block_root: Option<B256>,
/// Withdrawals
pub withdrawals: Option<Withdrawals>,
/// Optional extra data.
pub extra_data: Bytes,
}
/// Abstraction over transaction environment.

View File

@@ -17,6 +17,14 @@ pub struct ExecutorMetrics {
/// The Histogram for amount of gas used.
pub gas_used_histogram: Histogram,
/// The Histogram for amount of time taken to execute the pre-execution changes.
pub pre_execution_histogram: Histogram,
/// The Histogram for amount of time taken to wait for one transaction to be available.
pub transaction_wait_histogram: Histogram,
/// The Histogram for amount of time taken to execute one transaction.
pub transaction_execution_histogram: Histogram,
/// The Histogram for amount of time taken to execute the post-execution changes.
pub post_execution_histogram: Histogram,
/// The Histogram for amount of time taken to execute blocks.
pub execution_histogram: Histogram,
/// The total amount of time it took to execute the latest block.

View File

@@ -95,17 +95,33 @@ pub(super) mod serde_bincode_compat {
/// notification: ExExNotification<N>,
/// }
/// ```
///
/// This enum mirrors [`super::ExExNotification`] but uses borrowed [`Chain`] types
/// instead of `Arc<Chain>` for bincode compatibility.
#[derive(Debug, Serialize, Deserialize)]
#[expect(missing_docs)]
#[serde(bound = "")]
#[expect(clippy::large_enum_variant)]
pub enum ExExNotification<'a, N>
where
N: NodePrimitives,
{
ChainCommitted { new: Chain<'a, N> },
ChainReorged { old: Chain<'a, N>, new: Chain<'a, N> },
ChainReverted { old: Chain<'a, N> },
/// Chain got committed without a reorg, and only the new chain is returned.
ChainCommitted {
/// The new chain after commit.
new: Chain<'a, N>,
},
/// Chain got reorged, and both the old and the new chains are returned.
ChainReorged {
/// The old chain before reorg.
old: Chain<'a, N>,
/// The new chain after reorg.
new: Chain<'a, N>,
},
/// Chain got reverted, and only the old chain is returned.
ChainReverted {
/// The old chain before reversion.
old: Chain<'a, N>,
},
}
impl<'a, N> From<&'a super::ExExNotification<N>> for ExExNotification<'a, N>

View File

@@ -131,6 +131,8 @@ pub struct TransactionsManagerMetrics {
/// capacity. Note, this is not a limit to the number of inflight requests, but a health
/// measure.
pub(crate) capacity_pending_pool_imports: Counter,
/// The time it took to prepare transactions for import. This is mostly sender recovery.
pub(crate) pool_import_prepare_duration: Histogram,
/* ================ POLL DURATION ================ */

View File

@@ -1338,6 +1338,8 @@ where
let Some(peer) = self.peers.get_mut(&peer_id) else { return };
let mut transactions = transactions.0;
let start = Instant::now();
// mark the transactions as received
self.transaction_fetcher
.remove_hashes_from_transaction_fetcher(transactions.iter().map(|tx| tx.tx_hash()));
@@ -1370,21 +1372,6 @@ where
// reallocations
let mut new_txs = Vec::with_capacity(transactions.len());
for tx in transactions {
// recover transaction
let tx = match tx.try_into_recovered() {
Ok(tx) => tx,
Err(badtx) => {
trace!(target: "net::tx",
peer_id=format!("{peer_id:#}"),
hash=%badtx.tx_hash(),
client_version=%peer.client_version,
"failed ecrecovery for transaction"
);
has_bad_transactions = true;
continue
}
};
match self.transactions_by_peers.entry(*tx.tx_hash()) {
Entry::Occupied(mut entry) => {
// transaction was already inserted
@@ -1402,6 +1389,21 @@ where
} else {
// this is a new transaction that should be imported into the pool
// recover transaction
let tx = match tx.try_into_recovered() {
Ok(tx) => tx,
Err(badtx) => {
trace!(target: "net::tx",
peer_id=format!("{peer_id:#}"),
hash=%badtx.tx_hash(),
client_version=%peer.client_version,
"failed ecrecovery for transaction"
);
has_bad_transactions = true;
continue
}
};
let pool_transaction = Pool::Transaction::from_pooled(tx);
new_txs.push(pool_transaction);
@@ -1459,6 +1461,8 @@ where
if num_already_seen_by_peer > 0 {
self.report_already_seen(peer_id);
}
self.metrics.pool_import_prepare_duration.record(start.elapsed());
}
/// Processes a [`FetchEvent`].

View File

@@ -533,6 +533,27 @@ where
}
/// Modifies the addons with the given closure.
///
/// This method provides access to methods on the addons type that don't have
/// direct builder methods. It's useful for advanced configuration scenarios
/// where you need to call addon-specific methods.
///
/// # Examples
///
/// ```rust,ignore
/// use tower::layer::util::Identity;
///
/// let builder = NodeBuilder::new(config)
/// .with_types::<EthereumNode>()
/// .with_components(EthereumNode::components())
/// .with_add_ons(EthereumAddOns::default())
/// .map_add_ons(|addons| addons.with_rpc_middleware(Identity::default()));
/// ```
///
/// # See also
///
/// - [`NodeAddOns`] trait for available addon types
/// - [`crate::NodeBuilderWithComponents::extend_rpc_modules`] for RPC module configuration
pub fn map_add_ons<F>(self, f: F) -> Self
where
F: FnOnce(AO) -> AO,
@@ -579,10 +600,10 @@ where
/// .extend_rpc_modules(|ctx| {
/// // Access node components, so they can used by the CustomApi
/// let pool = ctx.pool().clone();
///
///
/// // Add custom RPC namespace
/// ctx.modules.merge_configured(CustomApi { pool }.into_rpc())?;
///
///
/// Ok(())
/// })
/// .build()?;
@@ -838,8 +859,8 @@ impl<Node: FullNodeTypes> BuilderContext<Node> {
.request_handler(self.provider().clone())
.split_with_handle();
self.executor.spawn_critical("p2p txpool", Box::pin(txpool));
self.executor.spawn_critical("p2p eth request handler", Box::pin(eth));
self.executor.spawn_critical_blocking("p2p txpool", Box::pin(txpool));
self.executor.spawn_critical_blocking("p2p eth request handler", Box::pin(eth));
let default_peers_path = self.config().datadir().known_peers();
let known_peers_file = self.config().network.persistent_peers_file(default_peers_path);

View File

@@ -235,6 +235,27 @@ where
}
/// Modifies the addons with the given closure.
///
/// This method provides access to methods on the addons type that don't have
/// direct builder methods. It's useful for advanced configuration scenarios
/// where you need to call addon-specific methods.
///
/// # Examples
///
/// ```rust,ignore
/// use tower::layer::util::Identity;
///
/// let builder = NodeBuilder::new(config)
/// .with_types::<EthereumNode>()
/// .with_components(EthereumNode::components())
/// .with_add_ons(EthereumAddOns::default())
/// .map_add_ons(|addons| addons.with_rpc_middleware(Identity::default()));
/// ```
///
/// # See also
///
/// - [`NodeAddOns`] trait for available addon types
/// - [`crate::NodeBuilderWithComponents::extend_rpc_modules`] for RPC module configuration
pub fn map_add_ons<F>(mut self, f: F) -> Self
where
F: FnOnce(AO) -> AO,

View File

@@ -1010,7 +1010,7 @@ where
.with_executor(Box::new(node.task_executor().clone()))
.with_evm_config(node.evm_config().clone())
.with_consensus(node.consensus().clone())
.build_with_auth_server(module_config, engine_api, eth_api);
.build_with_auth_server(module_config, engine_api, eth_api, engine_events.clone());
// in dev mode we generate 20 random dev-signer accounts
if config.dev.dev {
@@ -1179,6 +1179,7 @@ impl<'a, N: FullNodeComponents<Types: NodeTypes<ChainSpec: Hardforks + EthereumH
.proof_permits(self.config.proof_permits)
.gas_oracle_config(self.config.gas_oracle)
.max_batch_size(self.config.max_batch_size)
.max_blocking_io_requests(self.config.max_blocking_io_requests)
.pending_block_kind(self.config.pending_block_kind)
.raw_tx_forwarder(self.config.raw_tx_forwarder)
.evm_memory_limit(self.config.rpc_evm_memory_limit)
@@ -1188,10 +1189,7 @@ impl<'a, N: FullNodeComponents<Types: NodeTypes<ChainSpec: Hardforks + EthereumH
/// A `EthApi` that knows how to build `eth` namespace API from [`FullNodeComponents`].
pub trait EthApiBuilder<N: FullNodeComponents>: Default + Send + 'static {
/// The Ethapi implementation this builder will build.
type EthApi: EthApiTypes
+ FullEthApiServer<Provider = N::Provider, Pool = N::Pool>
+ Unpin
+ 'static;
type EthApi: FullEthApiServer<Provider = N::Provider, Pool = N::Pool>;
/// Builds the [`EthApiServer`](reth_rpc_api::eth::EthApiServer) from the given context.
fn build_eth_api(

View File

@@ -1,13 +1,198 @@
//! clap [Args](clap::Args) for engine purposes
use clap::Args;
use clap::{builder::Resettable, Args};
use reth_engine_primitives::{TreeConfig, DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE};
use std::sync::OnceLock;
use crate::node_config::{
DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB, DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
DEFAULT_PERSISTENCE_THRESHOLD, DEFAULT_RESERVED_CPU_CORES,
};
/// Global static engine defaults
static ENGINE_DEFAULTS: OnceLock<DefaultEngineValues> = OnceLock::new();
/// Default values for engine that can be customized
///
/// Global defaults can be set via [`DefaultEngineValues::try_init`].
#[derive(Debug, Clone)]
pub struct DefaultEngineValues {
persistence_threshold: u64,
memory_block_buffer_target: u64,
legacy_state_root_task_enabled: bool,
state_cache_disabled: bool,
prewarming_disabled: bool,
parallel_sparse_trie_disabled: bool,
state_provider_metrics: bool,
cross_block_cache_size: u64,
state_root_task_compare_updates: bool,
accept_execution_requests_hash: bool,
multiproof_chunking_enabled: bool,
multiproof_chunk_size: usize,
reserved_cpu_cores: usize,
precompile_cache_disabled: bool,
state_root_fallback: bool,
always_process_payload_attributes_on_canonical_head: bool,
allow_unwind_canonical_header: bool,
storage_worker_count: Option<usize>,
account_worker_count: Option<usize>,
}
impl DefaultEngineValues {
/// Initialize the global engine defaults with this configuration
pub fn try_init(self) -> Result<(), Self> {
ENGINE_DEFAULTS.set(self)
}
/// Get a reference to the global engine defaults
pub fn get_global() -> &'static Self {
ENGINE_DEFAULTS.get_or_init(Self::default)
}
/// Set the default persistence threshold
pub const fn with_persistence_threshold(mut self, v: u64) -> Self {
self.persistence_threshold = v;
self
}
/// Set the default memory block buffer target
pub const fn with_memory_block_buffer_target(mut self, v: u64) -> Self {
self.memory_block_buffer_target = v;
self
}
/// Set whether to enable legacy state root task by default
pub const fn with_legacy_state_root_task_enabled(mut self, v: bool) -> Self {
self.legacy_state_root_task_enabled = v;
self
}
/// Set whether to disable state cache by default
pub const fn with_state_cache_disabled(mut self, v: bool) -> Self {
self.state_cache_disabled = v;
self
}
/// Set whether to disable prewarming by default
pub const fn with_prewarming_disabled(mut self, v: bool) -> Self {
self.prewarming_disabled = v;
self
}
/// Set whether to disable parallel sparse trie by default
pub const fn with_parallel_sparse_trie_disabled(mut self, v: bool) -> Self {
self.parallel_sparse_trie_disabled = v;
self
}
/// Set whether to enable state provider metrics by default
pub const fn with_state_provider_metrics(mut self, v: bool) -> Self {
self.state_provider_metrics = v;
self
}
/// Set the default cross-block cache size in MB
pub const fn with_cross_block_cache_size(mut self, v: u64) -> Self {
self.cross_block_cache_size = v;
self
}
/// Set whether to compare state root task updates by default
pub const fn with_state_root_task_compare_updates(mut self, v: bool) -> Self {
self.state_root_task_compare_updates = v;
self
}
/// Set whether to accept execution requests hash by default
pub const fn with_accept_execution_requests_hash(mut self, v: bool) -> Self {
self.accept_execution_requests_hash = v;
self
}
/// Set whether to enable multiproof chunking by default
pub const fn with_multiproof_chunking_enabled(mut self, v: bool) -> Self {
self.multiproof_chunking_enabled = v;
self
}
/// Set the default multiproof chunk size
pub const fn with_multiproof_chunk_size(mut self, v: usize) -> Self {
self.multiproof_chunk_size = v;
self
}
/// Set the default number of reserved CPU cores
pub const fn with_reserved_cpu_cores(mut self, v: usize) -> Self {
self.reserved_cpu_cores = v;
self
}
/// Set whether to disable precompile cache by default
pub const fn with_precompile_cache_disabled(mut self, v: bool) -> Self {
self.precompile_cache_disabled = v;
self
}
/// Set whether to enable state root fallback by default
pub const fn with_state_root_fallback(mut self, v: bool) -> Self {
self.state_root_fallback = v;
self
}
/// Set whether to always process payload attributes on canonical head by default
pub const fn with_always_process_payload_attributes_on_canonical_head(
mut self,
v: bool,
) -> Self {
self.always_process_payload_attributes_on_canonical_head = v;
self
}
/// Set whether to allow unwinding canonical header by default
pub const fn with_allow_unwind_canonical_header(mut self, v: bool) -> Self {
self.allow_unwind_canonical_header = v;
self
}
/// Set the default storage worker count
pub const fn with_storage_worker_count(mut self, v: Option<usize>) -> Self {
self.storage_worker_count = v;
self
}
/// Set the default account worker count
pub const fn with_account_worker_count(mut self, v: Option<usize>) -> Self {
self.account_worker_count = v;
self
}
}
impl Default for DefaultEngineValues {
fn default() -> Self {
Self {
persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
legacy_state_root_task_enabled: false,
state_cache_disabled: false,
prewarming_disabled: false,
parallel_sparse_trie_disabled: false,
state_provider_metrics: false,
cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
state_root_task_compare_updates: false,
accept_execution_requests_hash: false,
multiproof_chunking_enabled: true,
multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
precompile_cache_disabled: false,
state_root_fallback: false,
always_process_payload_attributes_on_canonical_head: false,
allow_unwind_canonical_header: false,
storage_worker_count: None,
account_worker_count: None,
}
}
}
/// Parameters for configuring the engine driver.
#[derive(Debug, Clone, Args, PartialEq, Eq)]
#[command(next_help_heading = "Engine")]
@@ -18,15 +203,15 @@ pub struct EngineArgs {
///
/// To persist blocks as fast as the node receives them, set this value to zero. This will
/// cause more frequent DB writes.
#[arg(long = "engine.persistence-threshold", default_value_t = DEFAULT_PERSISTENCE_THRESHOLD)]
#[arg(long = "engine.persistence-threshold", default_value_t = DefaultEngineValues::get_global().persistence_threshold)]
pub persistence_threshold: u64,
/// Configure the target number of blocks to keep in memory.
#[arg(long = "engine.memory-block-buffer-target", default_value_t = DEFAULT_MEMORY_BLOCK_BUFFER_TARGET)]
#[arg(long = "engine.memory-block-buffer-target", default_value_t = DefaultEngineValues::get_global().memory_block_buffer_target)]
pub memory_block_buffer_target: u64,
/// Enable legacy state root
#[arg(long = "engine.legacy-state-root", default_value = "false")]
#[arg(long = "engine.legacy-state-root", default_value_t = DefaultEngineValues::get_global().legacy_state_root_task_enabled)]
pub legacy_state_root_task_enabled: bool,
/// CAUTION: This CLI flag has no effect anymore, use --engine.disable-caching-and-prewarming
@@ -35,8 +220,12 @@ pub struct EngineArgs {
#[deprecated]
pub caching_and_prewarming_enabled: bool,
/// Disable state cache
#[arg(long = "engine.disable-state-cache", default_value_t = DefaultEngineValues::get_global().state_cache_disabled)]
pub state_cache_disabled: bool,
/// Disable parallel prewarming
#[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming")]
#[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming", default_value_t = DefaultEngineValues::get_global().prewarming_disabled)]
pub prewarming_disabled: bool,
/// CAUTION: This CLI flag has no effect anymore, use --engine.disable-parallel-sparse-trie
@@ -46,38 +235,38 @@ pub struct EngineArgs {
pub parallel_sparse_trie_enabled: bool,
/// Disable the parallel sparse trie in the engine.
#[arg(long = "engine.disable-parallel-sparse-trie", default_value = "false")]
#[arg(long = "engine.disable-parallel-sparse-trie", default_value_t = DefaultEngineValues::get_global().parallel_sparse_trie_disabled)]
pub parallel_sparse_trie_disabled: bool,
/// Enable state provider latency metrics. This allows the engine to collect and report stats
/// about how long state provider calls took during execution, but this does introduce slight
/// overhead to state provider calls.
#[arg(long = "engine.state-provider-metrics", default_value = "false")]
#[arg(long = "engine.state-provider-metrics", default_value_t = DefaultEngineValues::get_global().state_provider_metrics)]
pub state_provider_metrics: bool,
/// Configure the size of cross-block cache in megabytes
#[arg(long = "engine.cross-block-cache-size", default_value_t = DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB)]
#[arg(long = "engine.cross-block-cache-size", default_value_t = DefaultEngineValues::get_global().cross_block_cache_size)]
pub cross_block_cache_size: u64,
/// Enable comparing trie updates from the state root task to the trie updates from the regular
/// state root calculation.
#[arg(long = "engine.state-root-task-compare-updates")]
#[arg(long = "engine.state-root-task-compare-updates", default_value_t = DefaultEngineValues::get_global().state_root_task_compare_updates)]
pub state_root_task_compare_updates: bool,
/// Enables accepting requests hash instead of an array of requests in `engine_newPayloadV4`.
#[arg(long = "engine.accept-execution-requests-hash")]
#[arg(long = "engine.accept-execution-requests-hash", default_value_t = DefaultEngineValues::get_global().accept_execution_requests_hash)]
pub accept_execution_requests_hash: bool,
/// Whether multiproof task should chunk proof targets.
#[arg(long = "engine.multiproof-chunking", default_value = "true")]
#[arg(long = "engine.multiproof-chunking", default_value_t = DefaultEngineValues::get_global().multiproof_chunking_enabled)]
pub multiproof_chunking_enabled: bool,
/// Multiproof task chunk size for proof targets.
#[arg(long = "engine.multiproof-chunk-size", default_value_t = DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE)]
#[arg(long = "engine.multiproof-chunk-size", default_value_t = DefaultEngineValues::get_global().multiproof_chunk_size)]
pub multiproof_chunk_size: usize,
/// Configure the number of reserved CPU cores for non-reth processes
#[arg(long = "engine.reserved-cpu-cores", default_value_t = DEFAULT_RESERVED_CPU_CORES)]
#[arg(long = "engine.reserved-cpu-cores", default_value_t = DefaultEngineValues::get_global().reserved_cpu_cores)]
pub reserved_cpu_cores: usize,
/// CAUTION: This CLI flag has no effect anymore, use --engine.disable-precompile-cache
@@ -87,11 +276,11 @@ pub struct EngineArgs {
pub precompile_cache_enabled: bool,
/// Disable precompile cache
#[arg(long = "engine.disable-precompile-cache", default_value = "false")]
#[arg(long = "engine.disable-precompile-cache", default_value_t = DefaultEngineValues::get_global().precompile_cache_disabled)]
pub precompile_cache_disabled: bool,
/// Enable state root fallback, useful for testing
#[arg(long = "engine.state-root-fallback", default_value = "false")]
#[arg(long = "engine.state-root-fallback", default_value_t = DefaultEngineValues::get_global().state_root_fallback)]
pub state_root_fallback: bool,
/// Always process payload attributes and begin a payload build process even if
@@ -101,51 +290,73 @@ pub struct EngineArgs {
/// Note: This is a no-op on OP Stack.
#[arg(
long = "engine.always-process-payload-attributes-on-canonical-head",
default_value = "false"
default_value_t = DefaultEngineValues::get_global().always_process_payload_attributes_on_canonical_head
)]
pub always_process_payload_attributes_on_canonical_head: bool,
/// Allow unwinding canonical header to ancestor during forkchoice updates.
/// See `TreeConfig::unwind_canonical_header` for more details.
#[arg(long = "engine.allow-unwind-canonical-header", default_value = "false")]
#[arg(long = "engine.allow-unwind-canonical-header", default_value_t = DefaultEngineValues::get_global().allow_unwind_canonical_header)]
pub allow_unwind_canonical_header: bool,
/// Configure the number of storage proof workers in the Tokio blocking pool.
/// If not specified, defaults to 2x available parallelism, clamped between 2 and 64.
#[arg(long = "engine.storage-worker-count")]
#[arg(long = "engine.storage-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().storage_worker_count.map(|v| v.to_string().into())))]
pub storage_worker_count: Option<usize>,
/// Configure the number of account proof workers in the Tokio blocking pool.
/// If not specified, defaults to the same count as storage workers.
#[arg(long = "engine.account-worker-count")]
#[arg(long = "engine.account-worker-count", default_value = Resettable::from(DefaultEngineValues::get_global().account_worker_count.map(|v| v.to_string().into())))]
pub account_worker_count: Option<usize>,
}
#[allow(deprecated)]
impl Default for EngineArgs {
fn default() -> Self {
let DefaultEngineValues {
persistence_threshold,
memory_block_buffer_target,
legacy_state_root_task_enabled,
state_cache_disabled,
prewarming_disabled,
parallel_sparse_trie_disabled,
state_provider_metrics,
cross_block_cache_size,
state_root_task_compare_updates,
accept_execution_requests_hash,
multiproof_chunking_enabled,
multiproof_chunk_size,
reserved_cpu_cores,
precompile_cache_disabled,
state_root_fallback,
always_process_payload_attributes_on_canonical_head,
allow_unwind_canonical_header,
storage_worker_count,
account_worker_count,
} = DefaultEngineValues::get_global().clone();
Self {
persistence_threshold: DEFAULT_PERSISTENCE_THRESHOLD,
memory_block_buffer_target: DEFAULT_MEMORY_BLOCK_BUFFER_TARGET,
legacy_state_root_task_enabled: false,
state_root_task_compare_updates: false,
persistence_threshold,
memory_block_buffer_target,
legacy_state_root_task_enabled,
state_root_task_compare_updates,
caching_and_prewarming_enabled: true,
prewarming_disabled: false,
state_cache_disabled,
prewarming_disabled,
parallel_sparse_trie_enabled: true,
parallel_sparse_trie_disabled: false,
state_provider_metrics: false,
cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE_MB,
accept_execution_requests_hash: false,
multiproof_chunking_enabled: true,
multiproof_chunk_size: DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE,
reserved_cpu_cores: DEFAULT_RESERVED_CPU_CORES,
parallel_sparse_trie_disabled,
state_provider_metrics,
cross_block_cache_size,
accept_execution_requests_hash,
multiproof_chunking_enabled,
multiproof_chunk_size,
reserved_cpu_cores,
precompile_cache_enabled: true,
precompile_cache_disabled: false,
state_root_fallback: false,
always_process_payload_attributes_on_canonical_head: false,
allow_unwind_canonical_header: false,
storage_worker_count: None,
account_worker_count: None,
precompile_cache_disabled,
state_root_fallback,
always_process_payload_attributes_on_canonical_head,
allow_unwind_canonical_header,
storage_worker_count,
account_worker_count,
}
}
}
@@ -157,6 +368,7 @@ impl EngineArgs {
.with_persistence_threshold(self.persistence_threshold)
.with_memory_block_buffer_target(self.memory_block_buffer_target)
.with_legacy_state_root(self.legacy_state_root_task_enabled)
.without_state_cache(self.state_cache_disabled)
.without_prewarming(self.prewarming_disabled)
.with_disable_parallel_sparse_trie(self.parallel_sparse_trie_disabled)
.with_state_provider_metrics(self.state_provider_metrics)
@@ -202,4 +414,66 @@ mod tests {
let args = CommandParser::<EngineArgs>::parse_from(["reth"]).args;
assert_eq!(args, default_args);
}
#[test]
#[allow(deprecated)]
fn engine_args() {
let args = EngineArgs {
persistence_threshold: 100,
memory_block_buffer_target: 50,
legacy_state_root_task_enabled: true,
caching_and_prewarming_enabled: true,
state_cache_disabled: true,
prewarming_disabled: true,
parallel_sparse_trie_enabled: true,
parallel_sparse_trie_disabled: true,
state_provider_metrics: true,
cross_block_cache_size: 256,
state_root_task_compare_updates: true,
accept_execution_requests_hash: true,
multiproof_chunking_enabled: true,
multiproof_chunk_size: 512,
reserved_cpu_cores: 4,
precompile_cache_enabled: true,
precompile_cache_disabled: true,
state_root_fallback: true,
always_process_payload_attributes_on_canonical_head: true,
allow_unwind_canonical_header: true,
storage_worker_count: Some(16),
account_worker_count: Some(8),
};
let parsed_args = CommandParser::<EngineArgs>::parse_from([
"reth",
"--engine.persistence-threshold",
"100",
"--engine.memory-block-buffer-target",
"50",
"--engine.legacy-state-root",
"--engine.disable-state-cache",
"--engine.disable-prewarming",
"--engine.disable-parallel-sparse-trie",
"--engine.state-provider-metrics",
"--engine.cross-block-cache-size",
"256",
"--engine.state-root-task-compare-updates",
"--engine.accept-execution-requests-hash",
"--engine.multiproof-chunking",
"--engine.multiproof-chunk-size",
"512",
"--engine.reserved-cpu-cores",
"4",
"--engine.disable-precompile-cache",
"--engine.state-root-fallback",
"--engine.always-process-payload-attributes-on-canonical-head",
"--engine.allow-unwind-canonical-header",
"--engine.storage-worker-count",
"16",
"--engine.account-worker-count",
"8",
])
.args;
assert_eq!(parsed_args, args);
}
}

View File

@@ -6,7 +6,7 @@ pub use network::{DiscoveryArgs, NetworkArgs};
/// RpcServerArg struct for configuring the RPC
mod rpc_server;
pub use rpc_server::RpcServerArgs;
pub use rpc_server::{DefaultRpcServerArgs, RpcServerArgs};
/// `RpcStateCacheArgs` struct for configuring RPC state cache
mod rpc_state_cache;
@@ -66,7 +66,7 @@ pub use benchmark_args::BenchmarkArgs;
/// EngineArgs for configuring the engine
mod engine;
pub use engine::EngineArgs;
pub use engine::{DefaultEngineValues, EngineArgs};
/// `RessArgs` for configuring ress subprotocol.
mod ress_args;

View File

@@ -119,6 +119,18 @@ pub struct NetworkArgs {
#[arg(long)]
pub max_inbound_peers: Option<usize>,
/// Maximum number of total peers (inbound + outbound).
///
/// Splits peers using approximately 2:1 inbound:outbound ratio. Cannot be used together with
/// `--max-outbound-peers` or `--max-inbound-peers`.
#[arg(
long,
value_name = "COUNT",
conflicts_with = "max_outbound_peers",
conflicts_with = "max_inbound_peers"
)]
pub max_peers: Option<usize>,
/// Max concurrent `GetPooledTransactions` requests.
#[arg(long = "max-tx-reqs", value_name = "COUNT", default_value_t = DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS, verbatim_doc_comment)]
pub max_concurrent_tx_requests: u32,
@@ -245,6 +257,34 @@ impl NetworkArgs {
bootnodes.into_iter().filter_map(|node| node.resolve_blocking().ok()).collect()
})
}
/// Returns the max inbound peers (2:1 ratio).
pub fn resolved_max_inbound_peers(&self) -> Option<usize> {
if let Some(max_peers) = self.max_peers {
if max_peers == 0 {
Some(0)
} else {
let outbound = (max_peers / 3).max(1);
Some(max_peers.saturating_sub(outbound))
}
} else {
self.max_inbound_peers
}
}
/// Returns the max outbound peers (1:2 ratio).
pub fn resolved_max_outbound_peers(&self) -> Option<usize> {
if let Some(max_peers) = self.max_peers {
if max_peers == 0 {
Some(0)
} else {
Some((max_peers / 3).max(1))
}
} else {
self.max_outbound_peers
}
}
/// Configures and returns a `TransactionsManagerConfig` based on the current settings.
pub const fn transactions_manager_config(&self) -> TransactionsManagerConfig {
TransactionsManagerConfig {
@@ -291,8 +331,8 @@ impl NetworkArgs {
.peers_config_with_basic_nodes_from_file(
self.persistent_peers_file(peers_file).as_deref(),
)
.with_max_inbound_opt(self.max_inbound_peers)
.with_max_outbound_opt(self.max_outbound_peers)
.with_max_inbound_opt(self.resolved_max_inbound_peers())
.with_max_outbound_opt(self.resolved_max_outbound_peers())
.with_ip_filter(ip_filter);
// Configure basic network stack
@@ -434,6 +474,7 @@ impl Default for NetworkArgs {
port: DEFAULT_DISCOVERY_PORT,
max_outbound_peers: None,
max_inbound_peers: None,
max_peers: None,
max_concurrent_tx_requests: DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS,
max_concurrent_tx_requests_per_peer: DEFAULT_MAX_COUNT_CONCURRENT_REQUESTS_PER_PEER,
soft_limit_byte_size_pooled_transactions_response:
@@ -758,6 +799,96 @@ mod tests {
assert!(args.disable_tx_gossip);
}
#[test]
fn parse_max_peers_flag() {
let args = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "90"]).args;
assert_eq!(args.max_peers, Some(90));
assert_eq!(args.max_outbound_peers, None);
assert_eq!(args.max_inbound_peers, None);
assert_eq!(args.resolved_max_outbound_peers(), Some(30));
assert_eq!(args.resolved_max_inbound_peers(), Some(60));
}
#[test]
fn max_peers_conflicts_with_outbound() {
let result = CommandParser::<NetworkArgs>::try_parse_from([
"reth",
"--max-peers",
"90",
"--max-outbound-peers",
"50",
]);
assert!(
result.is_err(),
"Should fail when both --max-peers and --max-outbound-peers are used"
);
}
#[test]
fn max_peers_conflicts_with_inbound() {
let result = CommandParser::<NetworkArgs>::try_parse_from([
"reth",
"--max-peers",
"90",
"--max-inbound-peers",
"30",
]);
assert!(
result.is_err(),
"Should fail when both --max-peers and --max-inbound-peers are used"
);
}
#[test]
fn max_peers_split_calculation() {
let args = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "90"]).args;
assert_eq!(args.max_peers, Some(90));
assert_eq!(args.resolved_max_outbound_peers(), Some(30));
assert_eq!(args.resolved_max_inbound_peers(), Some(60));
}
#[test]
fn max_peers_small_values() {
let args1 = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "1"]).args;
assert_eq!(args1.resolved_max_outbound_peers(), Some(1));
assert_eq!(args1.resolved_max_inbound_peers(), Some(0));
let args2 = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "2"]).args;
assert_eq!(args2.resolved_max_outbound_peers(), Some(1));
assert_eq!(args2.resolved_max_inbound_peers(), Some(1));
let args3 = CommandParser::<NetworkArgs>::parse_from(["reth", "--max-peers", "3"]).args;
assert_eq!(args3.resolved_max_outbound_peers(), Some(1));
assert_eq!(args3.resolved_max_inbound_peers(), Some(2));
}
#[test]
fn resolved_peers_without_max_peers() {
let args = CommandParser::<NetworkArgs>::parse_from([
"reth",
"--max-outbound-peers",
"75",
"--max-inbound-peers",
"15",
])
.args;
assert_eq!(args.max_peers, None);
assert_eq!(args.resolved_max_outbound_peers(), Some(75));
assert_eq!(args.resolved_max_inbound_peers(), Some(15));
}
#[test]
fn resolved_peers_with_defaults() {
let args = CommandParser::<NetworkArgs>::parse_from(["reth"]).args;
assert_eq!(args.max_peers, None);
assert_eq!(args.resolved_max_outbound_peers(), None);
assert_eq!(args.resolved_max_inbound_peers(), None);
}
#[test]
fn network_args_default_sanity_test() {
let default_args = NetworkArgs::default();

View File

@@ -7,7 +7,7 @@ use crate::args::{
use alloy_primitives::Address;
use alloy_rpc_types_engine::JwtSecret;
use clap::{
builder::{PossibleValue, RangedU64ValueParser, TypedValueParser},
builder::{PossibleValue, RangedU64ValueParser, Resettable, TypedValueParser},
Arg, Args, Command,
};
use rand::Rng;
@@ -19,12 +19,16 @@ use std::{
ffi::OsStr,
net::{IpAddr, Ipv4Addr},
path::PathBuf,
sync::OnceLock,
time::Duration,
};
use url::Url;
use super::types::MaxOr;
/// Global static RPC server defaults
static RPC_SERVER_DEFAULTS: OnceLock<DefaultRpcServerArgs> = OnceLock::new();
/// Default max number of subscriptions per connection.
pub(crate) const RPC_DEFAULT_MAX_SUBS_PER_CONN: u32 = 1024;
@@ -37,76 +41,442 @@ pub(crate) const RPC_DEFAULT_MAX_REQUEST_SIZE_MB: u32 = 15;
pub(crate) const RPC_DEFAULT_MAX_RESPONSE_SIZE_MB: u32 = 160;
/// Default number of incoming connections.
///
/// This restricts how many active connections (http, ws) the server accepts.
/// Once exceeded, the server can reject new connections.
pub(crate) const RPC_DEFAULT_MAX_CONNECTIONS: u32 = 500;
/// Default values for RPC server that can be customized
///
/// Global defaults can be set via [`DefaultRpcServerArgs::try_init`].
#[derive(Debug, Clone)]
pub struct DefaultRpcServerArgs {
http: bool,
http_addr: IpAddr,
http_port: u16,
http_disable_compression: bool,
http_api: Option<RpcModuleSelection>,
http_corsdomain: Option<String>,
ws: bool,
ws_addr: IpAddr,
ws_port: u16,
ws_allowed_origins: Option<String>,
ws_api: Option<RpcModuleSelection>,
ipcdisable: bool,
ipcpath: String,
ipc_socket_permissions: Option<String>,
auth_addr: IpAddr,
auth_port: u16,
auth_jwtsecret: Option<PathBuf>,
auth_ipc: bool,
auth_ipc_path: String,
disable_auth_server: bool,
rpc_jwtsecret: Option<JwtSecret>,
rpc_max_request_size: MaxU32,
rpc_max_response_size: MaxU32,
rpc_max_subscriptions_per_connection: MaxU32,
rpc_max_connections: MaxU32,
rpc_max_tracing_requests: usize,
rpc_max_blocking_io_requests: usize,
rpc_max_trace_filter_blocks: u64,
rpc_max_blocks_per_filter: ZeroAsNoneU64,
rpc_max_logs_per_response: ZeroAsNoneU64,
rpc_gas_cap: u64,
rpc_evm_memory_limit: u64,
rpc_tx_fee_cap: u128,
rpc_max_simulate_blocks: u64,
rpc_eth_proof_window: u64,
rpc_proof_permits: usize,
rpc_pending_block: PendingBlockKind,
rpc_forwarder: Option<Url>,
builder_disallow: Option<HashSet<Address>>,
rpc_state_cache: RpcStateCacheArgs,
gas_price_oracle: GasPriceOracleArgs,
rpc_send_raw_transaction_sync_timeout: Duration,
}
impl DefaultRpcServerArgs {
/// Initialize the global RPC server defaults with this configuration
pub fn try_init(self) -> Result<(), Self> {
RPC_SERVER_DEFAULTS.set(self)
}
/// Get a reference to the global RPC server defaults
pub fn get_global() -> &'static Self {
RPC_SERVER_DEFAULTS.get_or_init(Self::default)
}
/// Set the default HTTP enabled state
pub const fn with_http(mut self, v: bool) -> Self {
self.http = v;
self
}
/// Set the default HTTP address
pub const fn with_http_addr(mut self, v: IpAddr) -> Self {
self.http_addr = v;
self
}
/// Set the default HTTP port
pub const fn with_http_port(mut self, v: u16) -> Self {
self.http_port = v;
self
}
/// Set whether to disable HTTP compression by default
pub const fn with_http_disable_compression(mut self, v: bool) -> Self {
self.http_disable_compression = v;
self
}
/// Set the default HTTP API modules
pub fn with_http_api(mut self, v: Option<RpcModuleSelection>) -> Self {
self.http_api = v;
self
}
/// Set the default HTTP CORS domain
pub fn with_http_corsdomain(mut self, v: Option<String>) -> Self {
self.http_corsdomain = v;
self
}
/// Set the default WS enabled state
pub const fn with_ws(mut self, v: bool) -> Self {
self.ws = v;
self
}
/// Set the default WS address
pub const fn with_ws_addr(mut self, v: IpAddr) -> Self {
self.ws_addr = v;
self
}
/// Set the default WS port
pub const fn with_ws_port(mut self, v: u16) -> Self {
self.ws_port = v;
self
}
/// Set the default WS allowed origins
pub fn with_ws_allowed_origins(mut self, v: Option<String>) -> Self {
self.ws_allowed_origins = v;
self
}
/// Set the default WS API modules
pub fn with_ws_api(mut self, v: Option<RpcModuleSelection>) -> Self {
self.ws_api = v;
self
}
/// Set whether to disable IPC by default
pub const fn with_ipcdisable(mut self, v: bool) -> Self {
self.ipcdisable = v;
self
}
/// Set the default IPC path
pub fn with_ipcpath(mut self, v: String) -> Self {
self.ipcpath = v;
self
}
/// Set the default IPC socket permissions
pub fn with_ipc_socket_permissions(mut self, v: Option<String>) -> Self {
self.ipc_socket_permissions = v;
self
}
/// Set the default auth server address
pub const fn with_auth_addr(mut self, v: IpAddr) -> Self {
self.auth_addr = v;
self
}
/// Set the default auth server port
pub const fn with_auth_port(mut self, v: u16) -> Self {
self.auth_port = v;
self
}
/// Set the default auth JWT secret path
pub fn with_auth_jwtsecret(mut self, v: Option<PathBuf>) -> Self {
self.auth_jwtsecret = v;
self
}
/// Set the default auth IPC enabled state
pub const fn with_auth_ipc(mut self, v: bool) -> Self {
self.auth_ipc = v;
self
}
/// Set the default auth IPC path
pub fn with_auth_ipc_path(mut self, v: String) -> Self {
self.auth_ipc_path = v;
self
}
/// Set whether to disable the auth server by default
pub const fn with_disable_auth_server(mut self, v: bool) -> Self {
self.disable_auth_server = v;
self
}
/// Set the default RPC JWT secret
pub const fn with_rpc_jwtsecret(mut self, v: Option<JwtSecret>) -> Self {
self.rpc_jwtsecret = v;
self
}
/// Set the default max request size
pub const fn with_rpc_max_request_size(mut self, v: MaxU32) -> Self {
self.rpc_max_request_size = v;
self
}
/// Set the default max response size
pub const fn with_rpc_max_response_size(mut self, v: MaxU32) -> Self {
self.rpc_max_response_size = v;
self
}
/// Set the default max subscriptions per connection
pub const fn with_rpc_max_subscriptions_per_connection(mut self, v: MaxU32) -> Self {
self.rpc_max_subscriptions_per_connection = v;
self
}
/// Set the default max connections
pub const fn with_rpc_max_connections(mut self, v: MaxU32) -> Self {
self.rpc_max_connections = v;
self
}
/// Set the default max tracing requests
pub const fn with_rpc_max_tracing_requests(mut self, v: usize) -> Self {
self.rpc_max_tracing_requests = v;
self
}
/// Set the default max blocking IO requests
pub const fn with_rpc_max_blocking_io_requests(mut self, v: usize) -> Self {
self.rpc_max_blocking_io_requests = v;
self
}
/// Set the default max trace filter blocks
pub const fn with_rpc_max_trace_filter_blocks(mut self, v: u64) -> Self {
self.rpc_max_trace_filter_blocks = v;
self
}
/// Set the default max blocks per filter
pub const fn with_rpc_max_blocks_per_filter(mut self, v: ZeroAsNoneU64) -> Self {
self.rpc_max_blocks_per_filter = v;
self
}
/// Set the default max logs per response
pub const fn with_rpc_max_logs_per_response(mut self, v: ZeroAsNoneU64) -> Self {
self.rpc_max_logs_per_response = v;
self
}
/// Set the default gas cap
pub const fn with_rpc_gas_cap(mut self, v: u64) -> Self {
self.rpc_gas_cap = v;
self
}
/// Set the default EVM memory limit
pub const fn with_rpc_evm_memory_limit(mut self, v: u64) -> Self {
self.rpc_evm_memory_limit = v;
self
}
/// Set the default tx fee cap
pub const fn with_rpc_tx_fee_cap(mut self, v: u128) -> Self {
self.rpc_tx_fee_cap = v;
self
}
/// Set the default max simulate blocks
pub const fn with_rpc_max_simulate_blocks(mut self, v: u64) -> Self {
self.rpc_max_simulate_blocks = v;
self
}
/// Set the default eth proof window
pub const fn with_rpc_eth_proof_window(mut self, v: u64) -> Self {
self.rpc_eth_proof_window = v;
self
}
/// Set the default proof permits
pub const fn with_rpc_proof_permits(mut self, v: usize) -> Self {
self.rpc_proof_permits = v;
self
}
/// Set the default pending block kind
pub const fn with_rpc_pending_block(mut self, v: PendingBlockKind) -> Self {
self.rpc_pending_block = v;
self
}
/// Set the default RPC forwarder
pub fn with_rpc_forwarder(mut self, v: Option<Url>) -> Self {
self.rpc_forwarder = v;
self
}
/// Set the default builder disallow addresses
pub fn with_builder_disallow(mut self, v: Option<HashSet<Address>>) -> Self {
self.builder_disallow = v;
self
}
/// Set the default RPC state cache args
pub const fn with_rpc_state_cache(mut self, v: RpcStateCacheArgs) -> Self {
self.rpc_state_cache = v;
self
}
/// Set the default gas price oracle args
pub const fn with_gas_price_oracle(mut self, v: GasPriceOracleArgs) -> Self {
self.gas_price_oracle = v;
self
}
/// Set the default send raw transaction sync timeout
pub const fn with_rpc_send_raw_transaction_sync_timeout(mut self, v: Duration) -> Self {
self.rpc_send_raw_transaction_sync_timeout = v;
self
}
}
impl Default for DefaultRpcServerArgs {
fn default() -> Self {
Self {
http: false,
http_addr: Ipv4Addr::LOCALHOST.into(),
http_port: constants::DEFAULT_HTTP_RPC_PORT,
http_disable_compression: false,
http_api: None,
http_corsdomain: None,
ws: false,
ws_addr: Ipv4Addr::LOCALHOST.into(),
ws_port: constants::DEFAULT_WS_RPC_PORT,
ws_allowed_origins: None,
ws_api: None,
ipcdisable: false,
ipcpath: constants::DEFAULT_IPC_ENDPOINT.to_string(),
ipc_socket_permissions: None,
auth_addr: Ipv4Addr::LOCALHOST.into(),
auth_port: constants::DEFAULT_AUTH_PORT,
auth_jwtsecret: None,
auth_ipc: false,
auth_ipc_path: constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string(),
disable_auth_server: false,
rpc_jwtsecret: None,
rpc_max_request_size: RPC_DEFAULT_MAX_REQUEST_SIZE_MB.into(),
rpc_max_response_size: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB.into(),
rpc_max_subscriptions_per_connection: RPC_DEFAULT_MAX_SUBS_PER_CONN.into(),
rpc_max_connections: RPC_DEFAULT_MAX_CONNECTIONS.into(),
rpc_max_tracing_requests: constants::default_max_tracing_requests(),
rpc_max_blocking_io_requests: constants::DEFAULT_MAX_BLOCKING_IO_REQUEST,
rpc_max_trace_filter_blocks: constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS,
rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(),
rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(),
rpc_gas_cap: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
rpc_evm_memory_limit: (1 << 32) - 1,
rpc_tx_fee_cap: constants::DEFAULT_TX_FEE_CAP_WEI,
rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS,
rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,
rpc_proof_permits: constants::DEFAULT_PROOF_PERMITS,
rpc_pending_block: PendingBlockKind::Full,
rpc_forwarder: None,
builder_disallow: None,
rpc_state_cache: RpcStateCacheArgs::default(),
gas_price_oracle: GasPriceOracleArgs::default(),
rpc_send_raw_transaction_sync_timeout:
constants::RPC_DEFAULT_SEND_RAW_TX_SYNC_TIMEOUT_SECS,
}
}
}
/// Parameters for configuring the rpc more granularity via CLI
#[derive(Debug, Clone, Args, PartialEq, Eq)]
#[command(next_help_heading = "RPC")]
pub struct RpcServerArgs {
/// Enable the HTTP-RPC server
#[arg(long, default_value_if("dev", "true", "true"))]
#[arg(long, default_value_if("dev", "true", "true"), default_value_t = DefaultRpcServerArgs::get_global().http)]
pub http: bool,
/// Http server address to listen on
#[arg(long = "http.addr", default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))]
#[arg(long = "http.addr", default_value_t = DefaultRpcServerArgs::get_global().http_addr)]
pub http_addr: IpAddr,
/// Http server port to listen on
#[arg(long = "http.port", default_value_t = constants::DEFAULT_HTTP_RPC_PORT)]
#[arg(long = "http.port", default_value_t = DefaultRpcServerArgs::get_global().http_port)]
pub http_port: u16,
/// Disable compression for HTTP responses
#[arg(long = "http.disable-compression", default_value_t = false)]
#[arg(long = "http.disable-compression", default_value_t = DefaultRpcServerArgs::get_global().http_disable_compression)]
pub http_disable_compression: bool,
/// Rpc Modules to be configured for the HTTP server
#[arg(long = "http.api", value_parser = RpcModuleSelectionValueParser::default())]
#[arg(long = "http.api", value_parser = RpcModuleSelectionValueParser::default(), default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_api.as_ref().map(|v| v.to_string().into())))]
pub http_api: Option<RpcModuleSelection>,
/// Http Corsdomain to allow request from
#[arg(long = "http.corsdomain")]
#[arg(long = "http.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().http_corsdomain.as_ref().map(|v| v.to_string().into())))]
pub http_corsdomain: Option<String>,
/// Enable the WS-RPC server
#[arg(long)]
#[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ws)]
pub ws: bool,
/// Ws server address to listen on
#[arg(long = "ws.addr", default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))]
#[arg(long = "ws.addr", default_value_t = DefaultRpcServerArgs::get_global().ws_addr)]
pub ws_addr: IpAddr,
/// Ws server port to listen on
#[arg(long = "ws.port", default_value_t = constants::DEFAULT_WS_RPC_PORT)]
#[arg(long = "ws.port", default_value_t = DefaultRpcServerArgs::get_global().ws_port)]
pub ws_port: u16,
/// Origins from which to accept `WebSocket` requests
#[arg(id = "ws.origins", long = "ws.origins", alias = "ws.corsdomain")]
#[arg(id = "ws.origins", long = "ws.origins", alias = "ws.corsdomain", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ws_allowed_origins.as_ref().map(|v| v.to_string().into())))]
pub ws_allowed_origins: Option<String>,
/// Rpc Modules to be configured for the WS server
#[arg(long = "ws.api", value_parser = RpcModuleSelectionValueParser::default())]
#[arg(long = "ws.api", value_parser = RpcModuleSelectionValueParser::default(), default_value = Resettable::from(DefaultRpcServerArgs::get_global().ws_api.as_ref().map(|v| v.to_string().into())))]
pub ws_api: Option<RpcModuleSelection>,
/// Disable the IPC-RPC server
#[arg(long)]
#[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcdisable)]
pub ipcdisable: bool,
/// Filename for IPC socket/pipe within the datadir
#[arg(long, default_value_t = constants::DEFAULT_IPC_ENDPOINT.to_string())]
#[arg(long, default_value_t = DefaultRpcServerArgs::get_global().ipcpath.clone())]
pub ipcpath: String,
/// Set the permissions for the IPC socket file, in octal format.
///
/// If not specified, the permissions will be set by the system's umask.
#[arg(long = "ipc.permissions")]
#[arg(long = "ipc.permissions", default_value = Resettable::from(DefaultRpcServerArgs::get_global().ipc_socket_permissions.as_ref().map(|v| v.to_string().into())))]
pub ipc_socket_permissions: Option<String>,
/// Auth server address to listen on
#[arg(long = "authrpc.addr", default_value_t = IpAddr::V4(Ipv4Addr::LOCALHOST))]
#[arg(long = "authrpc.addr", default_value_t = DefaultRpcServerArgs::get_global().auth_addr)]
pub auth_addr: IpAddr,
/// Auth server port to listen on
#[arg(long = "authrpc.port", default_value_t = constants::DEFAULT_AUTH_PORT)]
#[arg(long = "authrpc.port", default_value_t = DefaultRpcServerArgs::get_global().auth_port)]
pub auth_port: u16,
/// Path to a JWT secret to use for the authenticated engine-API RPC server.
@@ -115,22 +485,22 @@ pub struct RpcServerArgs {
///
/// If no path is provided, a secret will be generated and stored in the datadir under
/// `<DIR>/<CHAIN_ID>/jwt.hex`. For mainnet this would be `~/.reth/mainnet/jwt.hex` by default.
#[arg(long = "authrpc.jwtsecret", value_name = "PATH", global = true, required = false)]
#[arg(long = "authrpc.jwtsecret", value_name = "PATH", global = true, required = false, default_value = Resettable::from(DefaultRpcServerArgs::get_global().auth_jwtsecret.as_ref().map(|v| v.to_string_lossy().into())))]
pub auth_jwtsecret: Option<PathBuf>,
/// Enable auth engine API over IPC
#[arg(long)]
#[arg(long, default_value_t = DefaultRpcServerArgs::get_global().auth_ipc)]
pub auth_ipc: bool,
/// Filename for auth IPC socket/pipe within the datadir
#[arg(long = "auth-ipc.path", default_value_t = constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string())]
#[arg(long = "auth-ipc.path", default_value_t = DefaultRpcServerArgs::get_global().auth_ipc_path.clone())]
pub auth_ipc_path: String,
/// Disable the auth/engine API server.
///
/// This will prevent the authenticated engine-API server from starting. Use this if you're
/// running a node that doesn't need to serve engine API requests.
#[arg(long = "disable-auth-server", alias = "disable-engine-api")]
#[arg(long = "disable-auth-server", alias = "disable-engine-api", default_value_t = DefaultRpcServerArgs::get_global().disable_auth_server)]
pub disable_auth_server: bool,
/// Hex encoded JWT secret to authenticate the regular RPC server(s), see `--http.api` and
@@ -138,23 +508,23 @@ pub struct RpcServerArgs {
///
/// This is __not__ used for the authenticated engine-API RPC server, see
/// `--authrpc.jwtsecret`.
#[arg(long = "rpc.jwtsecret", value_name = "HEX", global = true, required = false)]
#[arg(long = "rpc.jwtsecret", value_name = "HEX", global = true, required = false, default_value = Resettable::from(DefaultRpcServerArgs::get_global().rpc_jwtsecret.as_ref().map(|v| format!("{:?}", v).into())))]
pub rpc_jwtsecret: Option<JwtSecret>,
/// Set the maximum RPC request payload size for both HTTP and WS in megabytes.
#[arg(long = "rpc.max-request-size", alias = "rpc-max-request-size", default_value_t = RPC_DEFAULT_MAX_REQUEST_SIZE_MB.into())]
#[arg(long = "rpc.max-request-size", alias = "rpc-max-request-size", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_request_size)]
pub rpc_max_request_size: MaxU32,
/// Set the maximum RPC response payload size for both HTTP and WS in megabytes.
#[arg(long = "rpc.max-response-size", alias = "rpc-max-response-size", visible_alias = "rpc.returndata.limit", default_value_t = RPC_DEFAULT_MAX_RESPONSE_SIZE_MB.into())]
#[arg(long = "rpc.max-response-size", alias = "rpc-max-response-size", visible_alias = "rpc.returndata.limit", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_response_size)]
pub rpc_max_response_size: MaxU32,
/// Set the maximum concurrent subscriptions per connection.
#[arg(long = "rpc.max-subscriptions-per-connection", alias = "rpc-max-subscriptions-per-connection", default_value_t = RPC_DEFAULT_MAX_SUBS_PER_CONN.into())]
#[arg(long = "rpc.max-subscriptions-per-connection", alias = "rpc-max-subscriptions-per-connection", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_subscriptions_per_connection)]
pub rpc_max_subscriptions_per_connection: MaxU32,
/// Maximum number of RPC server connections.
#[arg(long = "rpc.max-connections", alias = "rpc-max-connections", value_name = "COUNT", default_value_t = RPC_DEFAULT_MAX_CONNECTIONS.into())]
#[arg(long = "rpc.max-connections", alias = "rpc-max-connections", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_connections)]
pub rpc_max_connections: MaxU32,
/// Maximum number of concurrent tracing requests.
@@ -163,19 +533,27 @@ pub struct RpcServerArgs {
/// Tracing requests are generally CPU bound.
/// Choosing a value that is higher than the available CPU cores can have a negative impact on
/// the performance of the node and affect the node's ability to maintain sync.
#[arg(long = "rpc.max-tracing-requests", alias = "rpc-max-tracing-requests", value_name = "COUNT", default_value_t = constants::default_max_tracing_requests())]
#[arg(long = "rpc.max-tracing-requests", alias = "rpc-max-tracing-requests", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_tracing_requests)]
pub rpc_max_tracing_requests: usize,
/// Maximum number of concurrent blocking IO requests.
///
/// Blocking IO requests include `eth_call`, `eth_estimateGas`, and similar methods that
/// require EVM execution. These are spawned as blocking tasks to avoid blocking the async
/// runtime.
#[arg(long = "rpc.max-blocking-io-requests", alias = "rpc-max-blocking-io-requests", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_blocking_io_requests)]
pub rpc_max_blocking_io_requests: usize,
/// Maximum number of blocks for `trace_filter` requests.
#[arg(long = "rpc.max-trace-filter-blocks", alias = "rpc-max-trace-filter-blocks", value_name = "COUNT", default_value_t = constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS)]
#[arg(long = "rpc.max-trace-filter-blocks", alias = "rpc-max-trace-filter-blocks", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_trace_filter_blocks)]
pub rpc_max_trace_filter_blocks: u64,
/// Maximum number of blocks that could be scanned per filter request. (0 = entire chain)
#[arg(long = "rpc.max-blocks-per-filter", alias = "rpc-max-blocks-per-filter", value_name = "COUNT", default_value_t = ZeroAsNoneU64::new(constants::DEFAULT_MAX_BLOCKS_PER_FILTER))]
#[arg(long = "rpc.max-blocks-per-filter", alias = "rpc-max-blocks-per-filter", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_blocks_per_filter)]
pub rpc_max_blocks_per_filter: ZeroAsNoneU64,
/// Maximum number of logs that can be returned in a single response. (0 = no limit)
#[arg(long = "rpc.max-logs-per-response", alias = "rpc-max-logs-per-response", value_name = "COUNT", default_value_t = ZeroAsNoneU64::new(constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64))]
#[arg(long = "rpc.max-logs-per-response", alias = "rpc-max-logs-per-response", value_name = "COUNT", default_value_t = DefaultRpcServerArgs::get_global().rpc_max_logs_per_response)]
pub rpc_max_logs_per_response: ZeroAsNoneU64,
/// Maximum gas limit for `eth_call` and call tracing RPC methods.
@@ -184,7 +562,7 @@ pub struct RpcServerArgs {
alias = "rpc-gascap",
value_name = "GAS_CAP",
value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
default_value_t = constants::gas_oracle::RPC_DEFAULT_GAS_CAP
default_value_t = DefaultRpcServerArgs::get_global().rpc_gas_cap
)]
pub rpc_gas_cap: u64,
@@ -194,7 +572,7 @@ pub struct RpcServerArgs {
alias = "rpc-evm-memory-limit",
value_name = "MEMORY_LIMIT",
value_parser = MaxOr::new(RangedU64ValueParser::<u64>::new().range(1..)),
default_value_t = (1 << 32) - 1
default_value_t = DefaultRpcServerArgs::get_global().rpc_evm_memory_limit
)]
pub rpc_evm_memory_limit: u64,
@@ -212,7 +590,7 @@ pub struct RpcServerArgs {
#[arg(
long = "rpc.max-simulate-blocks",
value_name = "BLOCKS_COUNT",
default_value_t = constants::DEFAULT_MAX_SIMULATE_BLOCKS
default_value_t = DefaultRpcServerArgs::get_global().rpc_max_simulate_blocks
)]
pub rpc_max_simulate_blocks: u64,
@@ -221,7 +599,7 @@ pub struct RpcServerArgs {
/// configured number of blocks from current tip (up to `tip - window`).
#[arg(
long = "rpc.eth-proof-window",
default_value_t = constants::DEFAULT_ETH_PROOF_WINDOW,
default_value_t = DefaultRpcServerArgs::get_global().rpc_eth_proof_window,
value_parser = RangedU64ValueParser::<u64>::new().range(..=constants::MAX_ETH_PROOF_WINDOW)
)]
pub rpc_eth_proof_window: u64,
@@ -243,7 +621,7 @@ pub struct RpcServerArgs {
/// Path to file containing disallowed addresses, json-encoded list of strings. Block
/// validation API will reject blocks containing transactions from these addresses.
#[arg(long = "builder.disallow", value_name = "PATH", value_parser = reth_cli_util::parsers::read_json_from_file::<HashSet<Address>>)]
#[arg(long = "builder.disallow", value_name = "PATH", value_parser = reth_cli_util::parsers::read_json_from_file::<HashSet<Address>>, default_value = Resettable::from(DefaultRpcServerArgs::get_global().builder_disallow.as_ref().map(|v| format!("{:?}", v).into())))]
pub builder_disallow: Option<HashSet<Address>>,
/// State cache configuration.
@@ -387,49 +765,93 @@ impl RpcServerArgs {
impl Default for RpcServerArgs {
fn default() -> Self {
let DefaultRpcServerArgs {
http,
http_addr,
http_port,
http_disable_compression,
http_api,
http_corsdomain,
ws,
ws_addr,
ws_port,
ws_allowed_origins,
ws_api,
ipcdisable,
ipcpath,
ipc_socket_permissions,
auth_addr,
auth_port,
auth_jwtsecret,
auth_ipc,
auth_ipc_path,
disable_auth_server,
rpc_jwtsecret,
rpc_max_request_size,
rpc_max_response_size,
rpc_max_subscriptions_per_connection,
rpc_max_connections,
rpc_max_tracing_requests,
rpc_max_blocking_io_requests,
rpc_max_trace_filter_blocks,
rpc_max_blocks_per_filter,
rpc_max_logs_per_response,
rpc_gas_cap,
rpc_evm_memory_limit,
rpc_tx_fee_cap,
rpc_max_simulate_blocks,
rpc_eth_proof_window,
rpc_proof_permits,
rpc_pending_block,
rpc_forwarder,
builder_disallow,
rpc_state_cache,
gas_price_oracle,
rpc_send_raw_transaction_sync_timeout,
} = DefaultRpcServerArgs::get_global().clone();
Self {
http: false,
http_addr: Ipv4Addr::LOCALHOST.into(),
http_port: constants::DEFAULT_HTTP_RPC_PORT,
http_disable_compression: false,
http_api: None,
http_corsdomain: None,
ws: false,
ws_addr: Ipv4Addr::LOCALHOST.into(),
ws_port: constants::DEFAULT_WS_RPC_PORT,
ws_allowed_origins: None,
ws_api: None,
ipcdisable: false,
ipcpath: constants::DEFAULT_IPC_ENDPOINT.to_string(),
ipc_socket_permissions: None,
auth_addr: Ipv4Addr::LOCALHOST.into(),
auth_port: constants::DEFAULT_AUTH_PORT,
auth_jwtsecret: None,
auth_ipc: false,
auth_ipc_path: constants::DEFAULT_ENGINE_API_IPC_ENDPOINT.to_string(),
disable_auth_server: false,
rpc_jwtsecret: None,
rpc_max_request_size: RPC_DEFAULT_MAX_REQUEST_SIZE_MB.into(),
rpc_max_response_size: RPC_DEFAULT_MAX_RESPONSE_SIZE_MB.into(),
rpc_max_subscriptions_per_connection: RPC_DEFAULT_MAX_SUBS_PER_CONN.into(),
rpc_max_connections: RPC_DEFAULT_MAX_CONNECTIONS.into(),
rpc_max_tracing_requests: constants::default_max_tracing_requests(),
rpc_max_trace_filter_blocks: constants::DEFAULT_MAX_TRACE_FILTER_BLOCKS,
rpc_max_blocks_per_filter: constants::DEFAULT_MAX_BLOCKS_PER_FILTER.into(),
rpc_max_logs_per_response: (constants::DEFAULT_MAX_LOGS_PER_RESPONSE as u64).into(),
rpc_gas_cap: constants::gas_oracle::RPC_DEFAULT_GAS_CAP,
rpc_evm_memory_limit: (1 << 32) - 1,
rpc_tx_fee_cap: constants::DEFAULT_TX_FEE_CAP_WEI,
rpc_max_simulate_blocks: constants::DEFAULT_MAX_SIMULATE_BLOCKS,
rpc_eth_proof_window: constants::DEFAULT_ETH_PROOF_WINDOW,
rpc_pending_block: PendingBlockKind::Full,
gas_price_oracle: GasPriceOracleArgs::default(),
rpc_state_cache: RpcStateCacheArgs::default(),
rpc_proof_permits: constants::DEFAULT_PROOF_PERMITS,
rpc_forwarder: None,
builder_disallow: Default::default(),
rpc_send_raw_transaction_sync_timeout:
constants::RPC_DEFAULT_SEND_RAW_TX_SYNC_TIMEOUT_SECS,
http,
http_addr,
http_port,
http_disable_compression,
http_api,
http_corsdomain,
ws,
ws_addr,
ws_port,
ws_allowed_origins,
ws_api,
ipcdisable,
ipcpath,
ipc_socket_permissions,
auth_addr,
auth_port,
auth_jwtsecret,
auth_ipc,
auth_ipc_path,
disable_auth_server,
rpc_jwtsecret,
rpc_max_request_size,
rpc_max_response_size,
rpc_max_subscriptions_per_connection,
rpc_max_connections,
rpc_max_tracing_requests,
rpc_max_blocking_io_requests,
rpc_max_trace_filter_blocks,
rpc_max_blocks_per_filter,
rpc_max_logs_per_response,
rpc_gas_cap,
rpc_evm_memory_limit,
rpc_tx_fee_cap,
rpc_max_simulate_blocks,
rpc_eth_proof_window,
rpc_proof_permits,
rpc_pending_block,
rpc_forwarder,
builder_disallow,
rpc_state_cache,
gas_price_oracle,
rpc_send_raw_transaction_sync_timeout,
}
}
}
@@ -542,4 +964,159 @@ mod tests {
let expected = 1_000_000_000_000_000_000u128;
assert_eq!(args.rpc_tx_fee_cap, expected); // 1 ETH default cap
}
#[test]
fn test_rpc_server_args() {
let args = RpcServerArgs {
http: true,
http_addr: "127.0.0.1".parse().unwrap(),
http_port: 8545,
http_disable_compression: false,
http_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
http_corsdomain: Some("*".to_string()),
ws: true,
ws_addr: "127.0.0.1".parse().unwrap(),
ws_port: 8546,
ws_allowed_origins: Some("*".to_string()),
ws_api: Some(RpcModuleSelection::try_from_selection(["eth", "admin"]).unwrap()),
ipcdisable: false,
ipcpath: "reth.ipc".to_string(),
ipc_socket_permissions: Some("0o666".to_string()),
auth_addr: "127.0.0.1".parse().unwrap(),
auth_port: 8551,
auth_jwtsecret: Some(std::path::PathBuf::from("/tmp/jwt.hex")),
auth_ipc: false,
auth_ipc_path: "engine.ipc".to_string(),
disable_auth_server: false,
rpc_jwtsecret: Some(
JwtSecret::from_hex(
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
)
.unwrap(),
),
rpc_max_request_size: 15u32.into(),
rpc_max_response_size: 160u32.into(),
rpc_max_subscriptions_per_connection: 1024u32.into(),
rpc_max_connections: 500u32.into(),
rpc_max_tracing_requests: 16,
rpc_max_blocking_io_requests: 256,
rpc_max_trace_filter_blocks: 4000,
rpc_max_blocks_per_filter: 1000u64.into(),
rpc_max_logs_per_response: 10000u64.into(),
rpc_gas_cap: 50_000_000,
rpc_evm_memory_limit: 256,
rpc_tx_fee_cap: 2_000_000_000_000_000_000u128,
rpc_max_simulate_blocks: 256,
rpc_eth_proof_window: 100_000,
rpc_proof_permits: 16,
rpc_pending_block: PendingBlockKind::Full,
rpc_forwarder: Some("http://localhost:8545".parse().unwrap()),
builder_disallow: None,
rpc_state_cache: RpcStateCacheArgs {
max_blocks: 5000,
max_receipts: 2000,
max_headers: 1000,
max_concurrent_db_requests: 512,
},
gas_price_oracle: GasPriceOracleArgs {
blocks: 20,
ignore_price: 2,
max_price: 500_000_000_000,
percentile: 60,
default_suggested_fee: None,
},
rpc_send_raw_transaction_sync_timeout: std::time::Duration::from_secs(30),
};
let parsed_args = CommandParser::<RpcServerArgs>::parse_from([
"reth",
"--http",
"--http.addr",
"127.0.0.1",
"--http.port",
"8545",
"--http.api",
"eth,admin",
"--http.corsdomain",
"*",
"--ws",
"--ws.addr",
"127.0.0.1",
"--ws.port",
"8546",
"--ws.origins",
"*",
"--ws.api",
"eth,admin",
"--ipcpath",
"reth.ipc",
"--ipc.permissions",
"0o666",
"--authrpc.addr",
"127.0.0.1",
"--authrpc.port",
"8551",
"--authrpc.jwtsecret",
"/tmp/jwt.hex",
"--auth-ipc.path",
"engine.ipc",
"--rpc.jwtsecret",
"0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"--rpc.max-request-size",
"15",
"--rpc.max-response-size",
"160",
"--rpc.max-subscriptions-per-connection",
"1024",
"--rpc.max-connections",
"500",
"--rpc.max-tracing-requests",
"16",
"--rpc.max-blocking-io-requests",
"256",
"--rpc.max-trace-filter-blocks",
"4000",
"--rpc.max-blocks-per-filter",
"1000",
"--rpc.max-logs-per-response",
"10000",
"--rpc.gascap",
"50000000",
"--rpc.evm-memory-limit",
"256",
"--rpc.txfeecap",
"2.0",
"--rpc.max-simulate-blocks",
"256",
"--rpc.eth-proof-window",
"100000",
"--rpc.proof-permits",
"16",
"--rpc.pending-block",
"full",
"--rpc.forwarder",
"http://localhost:8545",
"--rpc-cache.max-blocks",
"5000",
"--rpc-cache.max-receipts",
"2000",
"--rpc-cache.max-headers",
"1000",
"--rpc-cache.max-concurrent-db-requests",
"512",
"--gpo.blocks",
"20",
"--gpo.ignoreprice",
"2",
"--gpo.maxprice",
"500000000000",
"--gpo.percentile",
"60",
"--rpc.send-raw-transaction-sync-timeout",
"30s",
])
.args;
assert_eq!(parsed_args, args);
}
}

View File

@@ -70,7 +70,7 @@ use reth_chainspec::{
};
use reth_ethereum_forks::{ChainHardforks, EthereumHardfork, ForkCondition};
use reth_network_peers::NodeRecord;
use reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER;
use reth_optimism_primitives::L2_TO_L1_MESSAGE_PASSER_ADDRESS;
use reth_primitives_traits::{sync::LazyLock, SealedHeader};
/// Chain spec builder for a OP stack chain.
@@ -499,7 +499,7 @@ pub fn make_op_genesis_header(genesis: &Genesis, hardforks: &ChainHardforks) ->
// If Isthmus is active, overwrite the withdrawals root with the storage root of predeploy
// `L2ToL1MessagePasser.sol`
if hardforks.fork(OpHardfork::Isthmus).active_at_timestamp(header.timestamp) &&
let Some(predeploy) = genesis.alloc.get(&ADDRESS_L2_TO_L1_MESSAGE_PASSER) &&
let Some(predeploy) = genesis.alloc.get(&L2_TO_L1_MESSAGE_PASSER_ADDRESS) &&
let Some(storage) = &predeploy.storage
{
header.withdrawals_root =

View File

@@ -98,7 +98,7 @@ mod tests {
OP_SEPOLIA_CANYON_TIMESTAMP, OP_SEPOLIA_ECOTONE_TIMESTAMP, OP_SEPOLIA_ISTHMUS_TIMESTAMP,
OP_SEPOLIA_JOVIAN_TIMESTAMP,
};
use reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER;
use reth_optimism_primitives::L2_TO_L1_MESSAGE_PASSER_ADDRESS;
use tar_no_std::TarArchiveRef;
#[test]
@@ -106,7 +106,7 @@ mod tests {
let genesis = read_superchain_genesis("unichain", "mainnet").unwrap();
assert_eq!(genesis.config.chain_id, 130);
assert_eq!(genesis.timestamp, 1730748359);
assert!(genesis.alloc.contains_key(&ADDRESS_L2_TO_L1_MESSAGE_PASSER));
assert!(genesis.alloc.contains_key(&L2_TO_L1_MESSAGE_PASSER_ADDRESS));
}
#[test]
@@ -114,7 +114,7 @@ mod tests {
let genesis = read_superchain_genesis("funki", "mainnet").unwrap();
assert_eq!(genesis.config.chain_id, 33979);
assert_eq!(genesis.timestamp, 1721211095);
assert!(genesis.alloc.contains_key(&ADDRESS_L2_TO_L1_MESSAGE_PASSER));
assert!(genesis.alloc.contains_key(&L2_TO_L1_MESSAGE_PASSER_ADDRESS));
}
#[test]

View File

@@ -241,7 +241,7 @@ mod tests {
use alloy_consensus::{BlockBody, Eip658Value, Header, Receipt, TxEip7702, TxReceipt};
use alloy_eips::{eip4895::Withdrawals, eip7685::Requests};
use alloy_primitives::{Address, Bytes, Signature, U256};
use alloy_primitives::{Address, Bytes, Log, Signature, U256};
use op_alloy_consensus::{
encode_holocene_extra_data, encode_jovian_extra_data, OpTypedTransaction,
};
@@ -367,7 +367,7 @@ mod tests {
let beacon_consensus = OpBeaconConsensus::new(Arc::new(chain_spec));
let receipt = OpReceipt::Eip7702(Receipt {
let receipt = OpReceipt::Eip7702(Receipt::<Log> {
status: Eip658Value::success(),
cumulative_gas_used: GAS_USED,
logs: vec![],
@@ -436,7 +436,7 @@ mod tests {
let beacon_consensus = OpBeaconConsensus::new(Arc::new(chain_spec));
let receipt = OpReceipt::Eip7702(Receipt {
let receipt = OpReceipt::Eip7702(Receipt::<Log> {
status: Eip658Value::success(),
cumulative_gas_used: GAS_USED,
logs: vec![],
@@ -451,7 +451,9 @@ mod tests {
)),
gas_used: GAS_USED,
timestamp: u64::MAX,
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(&receipt)),
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(
&receipt.with_bloom_ref(),
)),
logs_bloom: receipt.bloom(),
..Default::default()
};
@@ -509,7 +511,7 @@ mod tests {
let beacon_consensus = OpBeaconConsensus::new(Arc::new(chain_spec));
let receipt = OpReceipt::Eip7702(Receipt {
let receipt = OpReceipt::Eip7702(Receipt::<Log> {
status: Eip658Value::success(),
cumulative_gas_used: 0,
logs: vec![],
@@ -526,7 +528,9 @@ mod tests {
)),
gas_used: 0,
timestamp: u64::MAX - 1,
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(&receipt)),
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(
&receipt.with_bloom_ref(),
)),
logs_bloom: receipt.bloom(),
extra_data: encode_jovian_extra_data(
Default::default(),
@@ -549,7 +553,9 @@ mod tests {
)),
gas_used: 0,
timestamp: u64::MAX,
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(&receipt)),
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(
&receipt.with_bloom_ref(),
)),
logs_bloom: receipt.bloom(),
parent_hash: parent.hash(),
..Default::default()
@@ -576,7 +582,7 @@ mod tests {
let beacon_consensus = OpBeaconConsensus::new(Arc::new(chain_spec));
let receipt = OpReceipt::Eip7702(Receipt {
let receipt = OpReceipt::Eip7702(Receipt::<Log> {
status: Eip658Value::success(),
cumulative_gas_used: 0,
logs: vec![],
@@ -593,7 +599,9 @@ mod tests {
)),
gas_used: 0,
timestamp: u64::MAX - 1,
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(&receipt)),
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(
&receipt.with_bloom_ref(),
)),
logs_bloom: receipt.bloom(),
extra_data: encode_jovian_extra_data(
Default::default(),
@@ -616,7 +624,9 @@ mod tests {
)),
gas_used: 0,
timestamp: u64::MAX,
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(&receipt)),
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(
&receipt.with_bloom_ref(),
)),
logs_bloom: receipt.bloom(),
parent_hash: parent.hash(),
..Default::default()
@@ -652,7 +662,7 @@ mod tests {
let beacon_consensus = OpBeaconConsensus::new(Arc::new(chain_spec));
let receipt = OpReceipt::Eip7702(Receipt {
let receipt = OpReceipt::Eip7702(Receipt::<Log> {
status: Eip658Value::success(),
cumulative_gas_used: 0,
logs: vec![],
@@ -669,7 +679,9 @@ mod tests {
)),
gas_used: 0,
timestamp: u64::MAX - 1,
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(&receipt)),
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(
&receipt.with_bloom_ref(),
)),
logs_bloom: receipt.bloom(),
extra_data: encode_jovian_extra_data(
Default::default(),
@@ -693,7 +705,9 @@ mod tests {
)),
gas_used: 0,
timestamp: u64::MAX,
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(&receipt)),
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(
&receipt.with_bloom_ref(),
)),
logs_bloom: receipt.bloom(),
parent_hash: parent.hash(),
..Default::default()
@@ -722,7 +736,7 @@ mod tests {
let beacon_consensus = OpBeaconConsensus::new(Arc::new(chain_spec));
let receipt = OpReceipt::Eip7702(Receipt {
let receipt = OpReceipt::Eip7702(Receipt::<Log> {
status: Eip658Value::success(),
cumulative_gas_used: 0,
logs: vec![],
@@ -739,7 +753,9 @@ mod tests {
)),
gas_used: 0,
timestamp: u64::MAX - 1,
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(&receipt)),
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(
&receipt.with_bloom_ref(),
)),
logs_bloom: receipt.bloom(),
extra_data: encode_holocene_extra_data(Default::default(), BaseFeeParams::optimism())
.unwrap(),
@@ -759,7 +775,9 @@ mod tests {
)),
gas_used: 0,
timestamp: u64::MAX,
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(&receipt)),
receipts_root: proofs::calculate_receipt_root(std::slice::from_ref(
&receipt.with_bloom_ref(),
)),
logs_bloom: receipt.bloom(),
parent_hash: parent.hash(),
..Default::default()

View File

@@ -4,7 +4,7 @@ use crate::OpConsensusError;
use alloy_consensus::BlockHeader;
use alloy_primitives::B256;
use alloy_trie::EMPTY_ROOT_HASH;
use reth_optimism_primitives::ADDRESS_L2_TO_L1_MESSAGE_PASSER;
use reth_optimism_primitives::L2_TO_L1_MESSAGE_PASSER_ADDRESS;
use reth_storage_api::{errors::ProviderResult, StorageRootProvider};
use reth_trie_common::HashedStorage;
use revm::database::BundleState;
@@ -32,7 +32,7 @@ pub fn withdrawals_root<DB: StorageRootProvider>(
withdrawals_root_prehashed(
state_updates
.state()
.get(&ADDRESS_L2_TO_L1_MESSAGE_PASSER)
.get(&L2_TO_L1_MESSAGE_PASSER_ADDRESS)
.map(|acc| {
HashedStorage::from_plain_storage(
acc.status,
@@ -52,7 +52,7 @@ pub fn withdrawals_root_prehashed<DB: StorageRootProvider>(
hashed_storage_updates: HashedStorage,
state: DB,
) -> ProviderResult<B256> {
state.storage_root(ADDRESS_L2_TO_L1_MESSAGE_PASSER, hashed_storage_updates)
state.storage_root(L2_TO_L1_MESSAGE_PASSER_ADDRESS, hashed_storage_updates)
}
/// Verifies block header field `withdrawals_root` against storage root of
@@ -146,7 +146,7 @@ mod test {
#[test]
fn l2tol1_message_passer_no_withdrawals() {
let hashed_address = keccak256(ADDRESS_L2_TO_L1_MESSAGE_PASSER);
let hashed_address = keccak256(L2_TO_L1_MESSAGE_PASSER_ADDRESS);
// create account storage
let init_storage = HashedStorage::from_iter(

View File

@@ -16,7 +16,7 @@ use alloy_consensus::{BlockHeader, Header};
use alloy_eips::Decodable2718;
use alloy_evm::{EvmFactory, FromRecoveredTx, FromTxWithEncoded};
use alloy_op_evm::block::{receipt_builder::OpReceiptBuilder, OpTxEnv};
use alloy_primitives::U256;
use alloy_primitives::{Bytes, U256};
use core::fmt::Debug;
use op_alloy_consensus::EIP1559ParamError;
use op_alloy_rpc_types_engine::OpExecutionData;
@@ -265,12 +265,15 @@ where
&self,
payload: &OpExecutionData,
) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
Ok(payload.payload.transactions().clone().into_iter().map(|encoded| {
let transactions = payload.payload.transactions().clone().into_iter();
let convert = |encoded: Bytes| {
let tx = TxTy::<Self::Primitives>::decode_2718_exact(encoded.as_ref())
.map_err(AnyError::new)?;
let signer = tx.try_recover().map_err(AnyError::new)?;
Ok::<_, AnyError>(WithEncoded::new(encoded, tx.with_signer(signer)))
}))
};
Ok((transactions, convert))
}
}
@@ -483,14 +486,14 @@ mod tests {
block2.set_hash(block2_hash);
// Create a random receipt object, receipt1
let receipt1 = OpReceipt::Legacy(Receipt {
let receipt1 = OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![],
status: true.into(),
});
// Create another random receipt object, receipt2
let receipt2 = OpReceipt::Legacy(Receipt {
let receipt2 = OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 1325345,
logs: vec![],
status: true.into(),
@@ -541,7 +544,7 @@ mod tests {
);
// Create a Receipts object with a vector of receipt vectors
let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![],
status: true.into(),
@@ -599,7 +602,7 @@ mod tests {
#[test]
fn test_block_number_to_index() {
// Create a Receipts object with a vector of receipt vectors
let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![],
status: true.into(),
@@ -630,7 +633,7 @@ mod tests {
#[test]
fn test_get_logs() {
// Create a Receipts object with a vector of receipt vectors
let receipts = vec![vec![OpReceipt::Legacy(Receipt {
let receipts = vec![vec![OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![Log::<LogData>::default()],
status: true.into(),
@@ -658,7 +661,7 @@ mod tests {
#[test]
fn test_receipts_by_block() {
// Create a Receipts object with a vector of receipt vectors
let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![Log::<LogData>::default()],
status: true.into(),
@@ -682,7 +685,7 @@ mod tests {
// Assert that the receipts for block number 123 match the expected receipts
assert_eq!(
receipts_by_block,
vec![&Some(OpReceipt::Legacy(Receipt {
vec![&Some(OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![Log::<LogData>::default()],
status: true.into(),
@@ -693,7 +696,7 @@ mod tests {
#[test]
fn test_receipts_len() {
// Create a Receipts object with a vector of receipt vectors
let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt {
let receipts = vec![vec![Some(OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![Log::<LogData>::default()],
status: true.into(),
@@ -738,7 +741,7 @@ mod tests {
#[test]
fn test_revert_to() {
// Create a random receipt object
let receipt = OpReceipt::Legacy(Receipt {
let receipt = OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![],
status: true.into(),
@@ -783,7 +786,7 @@ mod tests {
#[test]
fn test_extend_execution_outcome() {
// Create a Receipt object with specific attributes.
let receipt = OpReceipt::Legacy(Receipt {
let receipt = OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![],
status: true.into(),
@@ -823,7 +826,7 @@ mod tests {
#[test]
fn test_split_at_execution_outcome() {
// Create a random receipt object
let receipt = OpReceipt::Legacy(Receipt {
let receipt = OpReceipt::Legacy(Receipt::<Log> {
cumulative_gas_used: 46913,
logs: vec![],
status: true.into(),

View File

@@ -18,7 +18,7 @@ use reth_node_api::{
use reth_optimism_consensus::isthmus;
use reth_optimism_forks::OpHardforks;
use reth_optimism_payload_builder::{OpExecutionPayloadValidator, OpPayloadTypes};
use reth_optimism_primitives::{OpBlock, ADDRESS_L2_TO_L1_MESSAGE_PASSER};
use reth_optimism_primitives::{OpBlock, L2_TO_L1_MESSAGE_PASSER_ADDRESS};
use reth_primitives_traits::{Block, RecoveredBlock, SealedBlock, SignedTransaction};
use reth_provider::StateProviderFactory;
use reth_trie_common::{HashedPostState, KeyHasher};
@@ -76,7 +76,7 @@ pub struct OpEngineValidator<P, Tx, ChainSpec> {
impl<P, Tx, ChainSpec> OpEngineValidator<P, Tx, ChainSpec> {
/// Instantiates a new validator.
pub fn new<KH: KeyHasher>(chain_spec: Arc<ChainSpec>, provider: P) -> Self {
let hashed_addr_l2tol1_msg_passer = KH::hash_key(ADDRESS_L2_TO_L1_MESSAGE_PASSER);
let hashed_addr_l2tol1_msg_passer = KH::hash_key(L2_TO_L1_MESSAGE_PASSER_ADDRESS);
Self {
inner: OpExecutionPayloadValidator::new(chain_spec),
provider,

View File

@@ -20,7 +20,7 @@ use reth_evm::{
};
use reth_execution_types::ExecutionOutcome;
use reth_optimism_forks::OpHardforks;
use reth_optimism_primitives::{transaction::OpTransaction, ADDRESS_L2_TO_L1_MESSAGE_PASSER};
use reth_optimism_primitives::{transaction::OpTransaction, L2_TO_L1_MESSAGE_PASSER_ADDRESS};
use reth_optimism_txpool::{
estimated_da_size::DataAvailabilitySized,
interop::{is_valid_interop, MaybeInteropTransaction},
@@ -435,7 +435,7 @@ impl<Txs> OpBuilder<'_, Txs> {
if ctx.chain_spec.is_isthmus_active_at_timestamp(ctx.attributes().timestamp()) {
// force load `L2ToL1MessagePasser.sol` so l2 withdrawals root can be computed even if
// no l2 withdrawals in block
_ = db.load_cache_account(ADDRESS_L2_TO_L1_MESSAGE_PASSER)?;
_ = db.load_cache_account(L2_TO_L1_MESSAGE_PASSER_ADDRESS)?;
}
let ExecutionWitnessRecord { hashed_state, codes, keys, lowest_block_number: _ } =

View File

@@ -13,8 +13,8 @@ extern crate alloc;
pub mod bedrock;
pub mod predeploys;
pub use predeploys::ADDRESS_L2_TO_L1_MESSAGE_PASSER;
// Re-export predeploys from op-alloy-consensus
pub use op_alloy_consensus::L2_TO_L1_MESSAGE_PASSER_ADDRESS;
pub mod transaction;
pub use transaction::*;

View File

@@ -1,8 +0,0 @@
//! Addresses of OP pre-deploys.
// todo: move to op-alloy
use alloy_primitives::{address, Address};
/// The L2 contract `L2ToL1MessagePasser`, stores commitments to withdrawal transactions.
pub const ADDRESS_L2_TO_L1_MESSAGE_PASSER: Address =
address!("0x4200000000000000000000000000000000000016");

View File

@@ -176,7 +176,7 @@ mod tests {
let mut data = Vec::with_capacity(expected.length());
let receipt = ReceiptWithBloom {
receipt: OpReceipt::Legacy(Receipt {
receipt: OpReceipt::Legacy(Receipt::<Log> {
status: Eip658Value::Eip658(false),
cumulative_gas_used: 0x1,
logs: vec![Log::new_unchecked(
@@ -207,7 +207,7 @@ mod tests {
// EIP658Receipt
let expected = ReceiptWithBloom {
receipt: OpReceipt::Legacy(Receipt {
receipt: OpReceipt::Legacy(Receipt::<Log> {
status: Eip658Value::Eip658(false),
cumulative_gas_used: 0x1,
logs: vec![Log::new_unchecked(
@@ -235,7 +235,7 @@ mod tests {
// Deposit Receipt (post-regolith)
let expected = ReceiptWithBloom {
receipt: OpReceipt::Deposit(OpDepositReceipt {
inner: Receipt {
inner: Receipt::<Log> {
status: Eip658Value::Eip658(true),
cumulative_gas_used: 46913,
logs: vec![],
@@ -260,10 +260,10 @@ mod tests {
"b901117ef9010d0182b741b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0833d3bbf01"
);
// Deposit Receipt (post-regolith)
// Deposit Receipt (post-canyon)
let expected = ReceiptWithBloom {
receipt: OpReceipt::Deposit(OpDepositReceipt {
inner: Receipt {
inner: Receipt::<Log> {
status: Eip658Value::Eip658(true),
cumulative_gas_used: 46913,
logs: vec![],
@@ -284,7 +284,7 @@ mod tests {
#[test]
fn gigantic_receipt() {
let receipt = OpReceipt::Legacy(Receipt {
let receipt = OpReceipt::Legacy(Receipt::<Log> {
status: Eip658Value::Eip658(true),
cumulative_gas_used: 16747627,
logs: vec![
@@ -314,7 +314,7 @@ mod tests {
#[test]
fn test_encode_2718_length() {
let receipt = ReceiptWithBloom {
receipt: OpReceipt::Eip1559(Receipt {
receipt: OpReceipt::Eip1559(Receipt::<Log> {
status: Eip658Value::Eip658(true),
cumulative_gas_used: 21000,
logs: vec![],
@@ -331,7 +331,7 @@ mod tests {
// Test for legacy receipt as well
let legacy_receipt = ReceiptWithBloom {
receipt: OpReceipt::Legacy(Receipt {
receipt: OpReceipt::Legacy(Receipt::<Log> {
status: Eip658Value::Eip658(true),
cumulative_gas_used: 21000,
logs: vec![],

View File

@@ -272,6 +272,11 @@ where
fn tracing_task_guard(&self) -> &BlockingTaskGuard {
self.inner.eth_api.blocking_task_guard()
}
#[inline]
fn blocking_io_task_guard(&self) -> &Arc<tokio::sync::Semaphore> {
self.inner.eth_api.blocking_io_request_semaphore()
}
}
impl<N, Rpc> LoadFee for OpEthApi<N, Rpc>

View File

@@ -1,17 +1,16 @@
//! Loads and formats OP receipt RPC response.
use crate::{eth::RpcNodeCore, OpEthApi, OpEthApiError};
use alloy_consensus::{BlockHeader, Receipt, TxReceipt};
use alloy_consensus::{BlockHeader, Receipt, ReceiptWithBloom, TxReceipt};
use alloy_eips::eip2718::Encodable2718;
use alloy_rpc_types_eth::{Log, TransactionReceipt};
use op_alloy_consensus::{OpReceiptEnvelope, OpTransaction};
use op_alloy_consensus::{OpReceipt, OpTransaction};
use op_alloy_rpc_types::{L1BlockInfo, OpTransactionReceipt, OpTransactionReceiptFields};
use op_revm::estimate_tx_compressed_size;
use reth_chainspec::ChainSpecProvider;
use reth_node_api::NodePrimitives;
use reth_optimism_evm::RethL1BlockInfo;
use reth_optimism_forks::OpHardforks;
use reth_optimism_primitives::OpReceipt;
use reth_primitives_traits::SealedBlock;
use reth_rpc_eth_api::{
helpers::LoadReceipt,
@@ -270,7 +269,7 @@ impl OpReceiptFieldsBuilder {
#[derive(Debug)]
pub struct OpReceiptBuilder {
/// Core receipt, has all the fields of an L1 receipt and is the basis for the OP receipt.
pub core_receipt: TransactionReceipt<OpReceiptEnvelope<Log>>,
pub core_receipt: TransactionReceipt<ReceiptWithBloom<OpReceipt<Log>>>,
/// Additional OP receipt fields.
pub op_receipt_fields: OpTransactionReceiptFields,
}
@@ -294,24 +293,14 @@ impl OpReceiptBuilder {
let logs = Log::collect_for_receipt(next_log_index, meta, logs);
Receipt { status, cumulative_gas_used, logs }
};
match receipt {
OpReceipt::Legacy(receipt) => {
OpReceiptEnvelope::Legacy(map_logs(receipt).into_with_bloom())
}
OpReceipt::Eip2930(receipt) => {
OpReceiptEnvelope::Eip2930(map_logs(receipt).into_with_bloom())
}
OpReceipt::Eip1559(receipt) => {
OpReceiptEnvelope::Eip1559(map_logs(receipt).into_with_bloom())
}
OpReceipt::Eip7702(receipt) => {
OpReceiptEnvelope::Eip7702(map_logs(receipt).into_with_bloom())
}
OpReceipt::Deposit(receipt) => {
OpReceiptEnvelope::Deposit(receipt.map_inner(map_logs).into_with_bloom())
}
}
let mapped_receipt: OpReceipt<Log> = match receipt {
OpReceipt::Legacy(receipt) => OpReceipt::Legacy(map_logs(receipt)),
OpReceipt::Eip2930(receipt) => OpReceipt::Eip2930(map_logs(receipt)),
OpReceipt::Eip1559(receipt) => OpReceipt::Eip1559(map_logs(receipt)),
OpReceipt::Eip7702(receipt) => OpReceipt::Eip7702(map_logs(receipt)),
OpReceipt::Deposit(receipt) => OpReceipt::Deposit(receipt.map_inner(map_logs)),
};
mapped_receipt.into_with_bloom()
});
// In jovian, we're using the blob gas used field to store the current da

View File

@@ -3,7 +3,7 @@
use crate::{MessageValidationKind, PayloadAttributes};
use alloc::vec::Vec;
use alloy_eips::{eip1898::BlockWithParent, eip4895::Withdrawal, eip7685::Requests, BlockNumHash};
use alloy_primitives::B256;
use alloy_primitives::{Bytes, B256};
use alloy_rpc_types_engine::ExecutionData;
use core::fmt::Debug;
use serde::{de::DeserializeOwned, Serialize};
@@ -40,6 +40,11 @@ pub trait ExecutionPayload:
/// Returns `None` for pre-Shanghai blocks.
fn withdrawals(&self) -> Option<&Vec<Withdrawal>>;
/// Returns the access list included in this payload.
///
/// Returns `None` for pre-Amsterdam blocks.
fn block_access_list(&self) -> Option<&Bytes>;
/// Returns the beacon block root associated with this payload.
///
/// Returns `None` for pre-merge payloads.
@@ -69,6 +74,10 @@ impl ExecutionPayload for ExecutionData {
self.payload.withdrawals()
}
fn block_access_list(&self) -> Option<&Bytes> {
None
}
fn parent_beacon_block_root(&self) -> Option<B256> {
self.sidecar.parent_beacon_block_root()
}
@@ -172,6 +181,10 @@ impl ExecutionPayload for op_alloy_rpc_types_engine::OpExecutionData {
self.payload.as_v2().map(|p| &p.withdrawals)
}
fn block_access_list(&self) -> Option<&Bytes> {
None
}
fn parent_beacon_block_root(&self) -> Option<B256> {
self.sidecar.parent_beacon_block_root()
}

View File

@@ -198,6 +198,47 @@ pub trait BlockBody:
.collect()
})
}
/// Returns an iterator over `Recovered<&Transaction>` for all transactions in the block body.
///
/// This method recovers signers and returns an iterator without cloning transactions,
/// making it more efficient than [`BlockBody::recover_transactions`] when owned values are not
/// required.
///
/// # Errors
///
/// Returns an error if any transaction's signature is invalid.
fn recover_transactions_ref(
&self,
) -> Result<impl Iterator<Item = Recovered<&Self::Transaction>> + '_, RecoveryError> {
let signers = self.recover_signers()?;
Ok(self
.transactions()
.iter()
.zip(signers)
.map(|(tx, signer)| Recovered::new_unchecked(tx, signer)))
}
/// Returns an iterator over `Recovered<&Transaction>` for all transactions in the block body
/// _without ensuring that the signature has a low `s` value_.
///
/// This method recovers signers and returns an iterator without cloning transactions,
/// making it more efficient than recovering with owned transactions when owned values are not
/// required.
///
/// # Errors
///
/// Returns an error if any transaction's signature is invalid.
fn recover_transactions_unchecked_ref(
&self,
) -> Result<impl Iterator<Item = Recovered<&Self::Transaction>> + '_, RecoveryError> {
let signers = self.recover_signers_unchecked()?;
Ok(self
.transactions()
.iter()
.zip(signers)
.map(|(tx, signer)| Recovered::new_unchecked(tx, signer)))
}
}
impl<T, H> BlockBody for alloy_consensus::BlockBody<T, H>

View File

@@ -164,7 +164,7 @@ pub use alloy_primitives::{logs_bloom, Log, LogData};
pub mod proofs;
mod storage;
pub use storage::StorageEntry;
pub use storage::{StorageEntry, ValueWithSubKey};
pub mod sync;

View File

@@ -1,5 +1,17 @@
use alloy_primitives::{B256, U256};
/// Trait for `DupSort` table values that contain a subkey.
///
/// This trait allows extracting the subkey from a value during database iteration,
/// enabling proper range queries and filtering on `DupSort` tables.
pub trait ValueWithSubKey {
/// The type of the subkey.
type SubKey;
/// Extract the subkey from the value.
fn get_subkey(&self) -> Self::SubKey;
}
/// Account storage entry.
///
/// `key` is the subkey when used as a value in the `StorageChangeSets` table.
@@ -21,6 +33,14 @@ impl StorageEntry {
}
}
impl ValueWithSubKey for StorageEntry {
type SubKey = B256;
fn get_subkey(&self) -> Self::SubKey {
self.key
}
}
impl From<(B256, U256)> for StorageEntry {
fn from((key, value): (B256, U256)) -> Self {
Self { key, value }

View File

@@ -1,14 +1,14 @@
use crate::PruneLimiter;
use reth_db_api::{
cursor::{DbCursorRO, DbCursorRW, RangeWalker},
table::{Table, TableRow},
transaction::DbTxMut,
table::{DupSort, Table, TableRow},
transaction::{DbTx, DbTxMut},
DatabaseError,
};
use std::{fmt::Debug, ops::RangeBounds};
use tracing::debug;
pub(crate) trait DbTxPruneExt: DbTxMut {
pub(crate) trait DbTxPruneExt: DbTxMut + DbTx {
/// Prune the table for the specified pre-sorted key iterator.
///
/// Returns number of rows pruned.
@@ -123,9 +123,55 @@ pub(crate) trait DbTxPruneExt: DbTxMut {
Ok(false)
}
/// Prune a DUPSORT table for the specified key range.
///
/// Returns number of rows pruned.
fn prune_dupsort_table_with_range<T: DupSort>(
&self,
keys: impl RangeBounds<T::Key> + Clone + Debug,
limiter: &mut PruneLimiter,
mut delete_callback: impl FnMut(TableRow<T>),
) -> Result<(usize, bool), DatabaseError> {
let starting_entries = self.entries::<T>()?;
let mut cursor = self.cursor_dup_write::<T>()?;
let mut walker = cursor.walk_range(keys)?;
let done = loop {
if limiter.is_limit_reached() {
debug!(
target: "providers::db",
?limiter,
deleted_entries_limit = %limiter.is_deleted_entries_limit_reached(),
time_limit = %limiter.is_time_limit_reached(),
table = %T::NAME,
"Pruning limit reached"
);
break false
}
let Some(res) = walker.next() else { break true };
let row = res?;
walker.delete_current_duplicates()?;
limiter.increment_deleted_entries_count();
delete_callback(row);
};
debug!(
target: "providers::db",
table=?T::NAME,
cursor_current=?cursor.current(),
"done walking",
);
let ending_entries = self.entries::<T>()?;
Ok((starting_entries - ending_entries, done))
}
}
impl<Tx> DbTxPruneExt for Tx where Tx: DbTxMut {}
impl<Tx> DbTxPruneExt for Tx where Tx: DbTxMut + DbTx {}
#[cfg(test)]
mod tests {

View File

@@ -71,10 +71,9 @@ where
let mut last_storages_pruned_block = None;
let (storages_pruned, done) =
provider.tx_ref().prune_table_with_range::<tables::StoragesTrieChangeSets>(
provider.tx_ref().prune_dupsort_table_with_range::<tables::StoragesTrieChangeSets>(
storage_range,
&mut limiter,
|_| false,
|(BlockNumberHashedAddress((block_number, _)), _)| {
last_storages_pruned_block = Some(block_number);
},
@@ -90,10 +89,9 @@ where
.unwrap_or(block_range_end);
let (accounts_pruned, done) =
provider.tx_ref().prune_table_with_range::<tables::AccountsTrieChangeSets>(
provider.tx_ref().prune_dupsort_table_with_range::<tables::AccountsTrieChangeSets>(
block_range,
&mut limiter,
|_| false,
|row| last_accounts_pruned_block = row.0,
)?;

View File

@@ -38,7 +38,7 @@ pub enum HistoryType {
/// Default number of blocks to retain for merkle changesets.
/// This is used by both the `MerkleChangeSets` stage and the pruner segment.
pub const MERKLE_CHANGESETS_RETENTION_BLOCKS: u64 = 64;
pub const MERKLE_CHANGESETS_RETENTION_BLOCKS: u64 = 128;
/// Default pruning mode for merkle changesets
const fn default_merkle_changesets_mode() -> PruneMode {
@@ -95,13 +95,7 @@ pub struct PruneModes {
pub bodies_history: Option<PruneMode>,
/// Merkle Changesets pruning configuration for `AccountsTrieChangeSets` and
/// `StoragesTrieChangeSets`.
#[cfg_attr(
any(test, feature = "serde"),
serde(
default = "default_merkle_changesets_mode",
deserialize_with = "deserialize_prune_mode_with_min_blocks::<MERKLE_CHANGESETS_RETENTION_BLOCKS, _>"
)
)]
#[cfg_attr(any(test, feature = "serde"), serde(default = "default_merkle_changesets_mode"))]
pub merkle_changesets: PruneMode,
/// Receipts pruning configuration by retaining only those receipts that contain logs emitted
/// by the specified addresses, discarding others. This setting is overridden by `receipts`.
@@ -155,14 +149,15 @@ impl PruneModes {
/// Returns `true` if any migration was performed.
///
/// Currently migrates:
/// - `merkle_changesets`: `Distance(10064)` -> `Distance(64)`
pub fn migrate(&mut self) -> bool {
if self.merkle_changesets == PruneMode::Distance(MINIMUM_PRUNING_DISTANCE) {
/// - `merkle_changesets`: `Distance(n)` where `n < 128` or `n == 10064` -> `Distance(128)`
pub const fn migrate(&mut self) -> bool {
if let PruneMode::Distance(d) = self.merkle_changesets &&
(d < MERKLE_CHANGESETS_RETENTION_BLOCKS || d == MINIMUM_PRUNING_DISTANCE)
{
self.merkle_changesets = PruneMode::Distance(MERKLE_CHANGESETS_RETENTION_BLOCKS);
true
} else {
false
return true;
}
false
}
/// Returns an error if we can't unwind to the targeted block because the target block is
@@ -214,28 +209,6 @@ impl PruneModes {
}
}
/// Deserializes [`PruneMode`] and validates that the value is not less than the const
/// generic parameter `MIN_BLOCKS`. This parameter represents the number of blocks that needs to be
/// left in database after the pruning.
///
/// 1. For [`PruneMode::Full`], it fails if `MIN_BLOCKS > 0`.
/// 2. For [`PruneMode::Distance`], it fails if `distance < MIN_BLOCKS + 1`. `+ 1` is needed because
/// `PruneMode::Distance(0)` means that we leave zero blocks from the latest, meaning we have one
/// block in the database.
#[cfg(any(test, feature = "serde"))]
fn deserialize_prune_mode_with_min_blocks<
'de,
const MIN_BLOCKS: u64,
D: serde::Deserializer<'de>,
>(
deserializer: D,
) -> Result<PruneMode, D::Error> {
use serde::Deserialize;
let prune_mode = PruneMode::deserialize(deserializer)?;
serde_deserialize_validate::<MIN_BLOCKS, D>(&prune_mode)?;
Ok(prune_mode)
}
/// Deserializes [`Option<PruneMode>`] and validates that the value is not less than the const
/// generic parameter `MIN_BLOCKS`. This parameter represents the number of blocks that needs to be
/// left in database after the pruning.

View File

@@ -107,18 +107,13 @@ impl RpcServiceT for RpcService {
fn batch<'a>(&self, req: Batch<'a>) -> impl Future<Output = Self::BatchResponse> + Send + 'a {
let entries: Vec<_> = req.into_iter().collect();
let mut got_notif = false;
let mut batch_response = BatchResponseBuilder::new_with_limit(self.max_response_body_size);
let mut pending_calls: FuturesOrdered<_> = entries
.into_iter()
.filter_map(|v| match v {
Ok(BatchEntry::Call(call)) => Some(Either::Right(self.call(call))),
Ok(BatchEntry::Notification(_n)) => {
got_notif = true;
None
}
Ok(BatchEntry::Notification(_n)) => None,
Err(_err) => Some(Either::Left(async {
MethodResponse::error(Id::Null, ErrorObject::from(ErrorCode::InvalidRequest))
})),

View File

@@ -35,9 +35,11 @@ alloy-serde.workspace = true
alloy-rpc-types-beacon.workspace = true
alloy-rpc-types-engine.workspace = true
alloy-genesis.workspace = true
serde = { workspace = true, features = ["derive"] }
# misc
jsonrpsee = { workspace = true, features = ["server", "macros"] }
serde_json.workspace = true
[features]
client = [
@@ -45,3 +47,8 @@ client = [
"jsonrpsee/async-client",
"reth-rpc-eth-api/client",
]
[dev-dependencies]
serde_json = { workspace = true }
tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
jsonrpsee = { workspace = true, features = ["client", "async-client", "http-client"] }

View File

@@ -3,7 +3,7 @@ use alloy_genesis::ChainConfig;
use alloy_json_rpc::RpcObject;
use alloy_primitives::{Address, Bytes, B256};
use alloy_rpc_types_debug::ExecutionWitness;
use alloy_rpc_types_eth::{Block, Bundle, StateContext};
use alloy_rpc_types_eth::{Bundle, StateContext};
use alloy_rpc_types_trace::geth::{
BlockTraceResult, GethDebugTracingCallOptions, GethDebugTracingOptions, GethTrace, TraceResult,
};
@@ -38,7 +38,7 @@ pub trait DebugApi<TxReq: RpcObject> {
/// Returns an array of recent bad blocks that the client has seen on the network.
#[method(name = "getBadBlocks")]
async fn bad_blocks(&self) -> RpcResult<Vec<Block>>;
async fn bad_blocks(&self) -> RpcResult<Vec<serde_json::Value>>;
/// Returns the structured logs created during the execution of EVM between two blocks
/// (excluding start) as a JSON object.

View File

@@ -25,11 +25,14 @@ mod net;
mod otterscan;
mod reth;
mod rpc;
mod testing;
mod trace;
mod txpool;
mod validation;
mod web3;
pub use testing::{TestingBuildBlockRequestV1, TESTING_BUILD_BLOCK_V1};
/// re-export of all server traits
pub use servers::*;
@@ -45,6 +48,7 @@ pub mod servers {
otterscan::OtterscanServer,
reth::RethApiServer,
rpc::RpcApiServer,
testing::TestingApiServer,
trace::TraceApiServer,
txpool::TxPoolApiServer,
validation::BlockSubmissionValidationApiServer,
@@ -75,6 +79,7 @@ pub mod clients {
otterscan::OtterscanClient,
reth::RethApiClient,
rpc::RpcApiServer,
testing::TestingApiClient,
trace::TraceApiClient,
txpool::TxPoolApiClient,
validation::BlockSubmissionValidationApiClient,

View File

@@ -0,0 +1,45 @@
//! Testing namespace for building a block in a single call.
//!
//! This follows the `testing_buildBlockV1` specification. **Highly sensitive:**
//! testing-only, powerful enough to include arbitrary transactions; must stay
//! disabled by default and never be exposed on public-facing RPC without an
//! explicit operator flag.
use alloy_primitives::{Bytes, B256};
use alloy_rpc_types_engine::{
ExecutionPayloadEnvelopeV5, PayloadAttributes as EthPayloadAttributes,
};
use jsonrpsee::proc_macros::rpc;
use serde::{Deserialize, Serialize};
/// Capability string for `testing_buildBlockV1`.
pub const TESTING_BUILD_BLOCK_V1: &str = "testing_buildBlockV1";
/// Request payload for `testing_buildBlockV1`.
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct TestingBuildBlockRequestV1 {
/// Parent block hash of the block to build.
pub parent_block_hash: B256,
/// Payload attributes (Cancun version).
pub payload_attributes: EthPayloadAttributes,
/// Raw signed transactions to force-include in order.
pub transactions: Vec<Bytes>,
/// Optional extra data for the block header.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extra_data: Option<Bytes>,
}
/// Testing RPC interface for building a block in a single call.
#[cfg_attr(not(feature = "client"), rpc(server, namespace = "testing"))]
#[cfg_attr(feature = "client", rpc(server, client, namespace = "testing"))]
pub trait TestingApi {
/// Builds a block using the provided parent, payload attributes, and transactions.
///
/// See <https://github.com/marcindsobczak/execution-apis/blob/main/src/testing/testing_buildBlockV1.md>
#[method(name = "buildBlockV1")]
async fn build_block_v1(
&self,
request: TestingBuildBlockRequestV1,
) -> jsonrpsee::core::RpcResult<ExecutionPayloadEnvelopeV5>;
}

View File

@@ -17,6 +17,7 @@ reth-primitives-traits.workspace = true
reth-ipc.workspace = true
reth-chainspec.workspace = true
reth-consensus.workspace = true
reth-engine-primitives.workspace = true
reth-network-api.workspace = true
reth-node-core.workspace = true
reth-rpc.workspace = true
@@ -26,6 +27,7 @@ reth-rpc-layer.workspace = true
reth-rpc-eth-types.workspace = true
reth-rpc-server-types.workspace = true
reth-tasks = { workspace = true, features = ["rayon"] }
reth-tokio-util.workspace = true
reth-transaction-pool.workspace = true
reth-storage-api.workspace = true
reth-chain-state.workspace = true
@@ -63,7 +65,6 @@ reth-rpc-api = { workspace = true, features = ["client"] }
reth-rpc-engine-api.workspace = true
reth-tracing.workspace = true
reth-transaction-pool = { workspace = true, features = ["test-utils"] }
reth-engine-primitives.workspace = true
reth-node-ethereum.workspace = true
alloy-primitives.workspace = true

View File

@@ -94,6 +94,7 @@ impl RethRpcServerConfig for RpcServerArgs {
fn eth_config(&self) -> EthConfig {
EthConfig::default()
.max_tracing_requests(self.rpc_max_tracing_requests)
.max_blocking_io_requests(self.rpc_max_blocking_io_requests)
.max_trace_filter_blocks(self.rpc_max_trace_filter_blocks)
.max_blocks_per_filter(self.rpc_max_blocks_per_filter.unwrap_or_max())
.max_logs_per_response(self.rpc_max_logs_per_response.unwrap_or_max() as usize)

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