Compare commits

..

16 Commits

Author SHA1 Message Date
yongkangc
9bd5a3ecba fix: strengthen batch invariants and prevent blinded node starvation
- Change debug_assert to assert for multi_added_removed_keys Arc equality
  check in BatchedStorageProof::merge, ensuring incorrect proofs are
  caught in release builds, not just debug

- Change BatchedAccountProof::merge to try_merge returning Result, properly
  handling incompatible caches by processing as separate batches instead
  of panicking

- Add MAX_DEFERRED_BLINDED_NODES (16) limit to prevent starvation of
  blinded node requests under high proof load - stops batching early when
  limit reached

- Pre-allocate deferred_blinded_nodes vectors with capacity

- Remove unnecessary clone of storage_work_tx by taking reference
2025-12-09 11:32:31 +00:00
yongkangc
1a5d9a3ad3 perf(trie): use Arc for proof sharing and add batch validation
- Wrap DecodedMultiProof and DecodedStorageMultiProof in Arc within ProofResult
  for O(1) cloning when sending batched results to multiple receivers
- Add debug assertions in BatchedStorageProof::merge and BatchedAccountProof::merge
  to validate that all batched jobs share the same Arc for multi_added_removed_keys
  and missed_leaves_storage_roots (critical invariants for correctness)
- Unwrap Arc at extraction sites using try_unwrap for zero-cost when sole owner
2025-12-09 11:32:31 +00:00
yongkangc
394b0d5989 perf(trie): batch proof jobs at worker level
Implement worker-level batching for both storage and account proofs to
reduce redundant trie traversals when multiple proof requests queue up.

When proof requests arrive faster than workers can process them, jobs
for the same account (storage proofs) or consecutive jobs (account proofs)
are now merged into single proof computations.
2025-12-09 11:32:31 +00:00
Arsenii Kulikov
bb4285c9ba feat: add helper method to eth validator (#20206) 2025-12-09 11:32:31 +00:00
Karl Yu
88dfd15243 feat: add StorageSettings for StoragesHistory in RocksDB (#20154) 2025-12-09 11:32:31 +00:00
futreall
388507bcee fix(rpc): return error if toBlock exceeds current head (#20202) 2025-12-09 11:32:31 +00:00
Léa Narzis
a430fb6798 test(era): complete int tests with roundtrip mainnet era files (#20064) 2025-12-09 11:32:31 +00:00
joshieDo
b6dca7b319 fix: set merkle changesets distance minimum to 128 (#20200) 2025-12-09 11:32:31 +00:00
Matthias Seitz
12fdca7e34 perf: avoid duplicate storage get call (#20180) 2025-12-09 11:32:31 +00:00
Matthias Seitz
c0d73fa5fe fix: trace filter range off by one (#20199) 2025-12-09 11:32:31 +00:00
forkfury
0cbd2aecdf feat(primitives-traits): add recover_transactions_ref to avoid cloning (#20187)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
2025-12-09 11:32:31 +00:00
Matthias Seitz
3094cd68ea fix: make inserted blocks part of fcu canonical (#20164)
Co-authored-by: Alexey Shekhirin <5773434+shekhirin@users.noreply.github.com>
2025-12-09 11:32:31 +00:00
Arsenii Kulikov
9265700ff9 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-09 11:32:31 +00:00
Alexey Shekhirin
0b07657f33 ci: run on ubuntu instead of reth runner (#20196) 2025-12-09 11:32:31 +00:00
github-actions[bot]
e7bd168eca chore(deps): weekly cargo update (#20174)
Co-authored-by: github-merge-queue <118344674+github-merge-queue@users.noreply.github.com>
2025-12-09 11:32:31 +00:00
yongkangc
9a55cb9da5 Add bench compare latency stats 2025-12-07 02:17:06 +00:00
285 changed files with 2544 additions and 9472 deletions

View File

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

View File

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

View File

@@ -11,19 +11,17 @@ env:
CARGO_TERM_COLOR: always
BASELINE: base
SEED: reth
RUSTC_WRAPPER: "sccache"
name: bench
jobs:
codspeed:
runs-on: depot-ubuntu-latest
runs-on: 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,12 +10,9 @@ on:
types: [opened, reopened, synchronize, closed]
merge_group:
env:
RUSTC_WRAPPER: "sccache"
jobs:
build:
runs-on: depot-ubuntu-latest-8
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Checkout
@@ -36,8 +33,6 @@ 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,11 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
name: compact-codec
jobs:
compact-codec:
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
strategy:
matrix:
bin:
@@ -27,7 +26,6 @@ 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,7 +11,6 @@ on:
env:
CARGO_TERM_COLOR: always
SEED: rustethereumethereumrust
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -20,14 +19,13 @@ concurrency:
jobs:
test:
name: e2e-testsuite
runs-on: depot-ubuntu-latest-4
runs-on: ubuntu-latest
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:

View File

@@ -24,7 +24,7 @@ jobs:
prepare-hive:
if: github.repository == 'paradigmxyz/reth'
timeout-minutes: 45
runs-on: depot-ubuntu-latest-16
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Checkout hive tests
@@ -44,7 +44,7 @@ jobs:
- name: Restore hive assets cache
id: cache-hive
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: ./hive_assets
key: hive-assets-${{ steps.hive-commit.outputs.hash }}-${{ hashFiles('.github/assets/hive/build_simulators.sh') }}
@@ -67,7 +67,7 @@ jobs:
chmod +x hive
- name: Upload hive assets
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: hive_assets
path: ./hive_assets
@@ -178,7 +178,7 @@ jobs:
- prepare-reth
- prepare-hive
name: run ${{ matrix.scenario.sim }}${{ matrix.scenario.limit && format(' - {0}', matrix.scenario.limit) }}
runs-on: depot-ubuntu-latest-16
runs-on: ubuntu-latest
permissions:
issues: write
steps:
@@ -187,13 +187,13 @@ jobs:
fetch-depth: 0
- name: Download hive assets
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
name: hive_assets
path: /tmp
- name: Download reth image
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
name: artifacts
path: /tmp

View File

@@ -14,7 +14,6 @@ on:
env:
CARGO_TERM_COLOR: always
SEED: rustethereumethereumrust
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -24,7 +23,7 @@ jobs:
test:
name: test / ${{ matrix.network }}
if: github.event_name != 'schedule'
runs-on: depot-ubuntu-latest-4
runs-on: ubuntu-latest
env:
RUST_BACKTRACE: 1
strategy:
@@ -38,7 +37,6 @@ 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
@@ -76,7 +74,6 @@ 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

@@ -32,7 +32,7 @@ jobs:
strategy:
fail-fast: false
name: run kurtosis
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
needs:
- prepare-reth
steps:
@@ -41,7 +41,7 @@ jobs:
fetch-depth: 0
- name: Download reth image
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
name: artifacts
path: /tmp

View File

@@ -30,7 +30,7 @@ jobs:
strategy:
fail-fast: false
name: run kurtosis
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
needs:
- prepare-reth
steps:
@@ -39,7 +39,7 @@ jobs:
fetch-depth: 0
- name: Download reth image
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
with:
name: artifacts
path: /tmp

View File

@@ -8,12 +8,11 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
jobs:
clippy-binaries:
name: clippy binaries / ${{ matrix.type }}
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
@@ -27,7 +26,6 @@ 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
@@ -42,7 +40,7 @@ jobs:
clippy:
name: clippy
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
@@ -50,7 +48,6 @@ 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
@@ -59,7 +56,7 @@ jobs:
RUSTFLAGS: -D warnings
wasm:
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
@@ -68,7 +65,6 @@ 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
@@ -79,7 +75,7 @@ jobs:
.github/assets/check_wasm.sh
riscv:
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
@@ -88,7 +84,6 @@ 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
@@ -98,18 +93,17 @@ jobs:
crate-checks:
name: crate-checks (${{ matrix.partition }}/${{ matrix.total_partitions }})
runs-on: depot-ubuntu-latest-4
runs-on: ubuntu-latest
strategy:
matrix:
partition: [1, 2, 3]
total_partitions: [3]
partition: [1, 2]
total_partitions: [2]
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
@@ -117,7 +111,7 @@ jobs:
msrv:
name: MSRV
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
@@ -130,7 +124,6 @@ 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
@@ -140,13 +133,12 @@ jobs:
docs:
name: docs
runs-on: depot-ubuntu-latest-4
runs-on: 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
@@ -158,7 +150,7 @@ jobs:
fmt:
name: fmt
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v6
@@ -166,19 +158,17 @@ 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: depot-ubuntu-latest
runs-on: 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
@@ -187,13 +177,12 @@ jobs:
book:
name: book
runs-on: depot-ubuntu-latest
runs-on: 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
@@ -243,39 +232,31 @@ 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 crates can compile with power set of features
# Checks that selected rates can compile with power set of features
features:
name: features
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
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: |
cargo hack check \
--package reth-codecs \
--package reth-primitives-traits \
--package reth-primitives \
--feature-powerset \
--depth 2
- run: make check-features
env:
RUSTFLAGS: -D warnings
# Check crates correctly propagate features
feature-propagation:
runs-on: depot-ubuntu-latest
runs-on: 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,7 +26,7 @@ jobs:
prepare-reth:
if: github.repository == 'paradigmxyz/reth'
timeout-minutes: 45
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: mkdir artifacts
@@ -50,7 +50,7 @@ jobs:
- name: Upload reth image
id: upload
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: artifacts
path: ./artifacts

View File

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

View File

@@ -22,7 +22,6 @@ 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:
@@ -52,7 +51,6 @@ 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
@@ -106,7 +104,6 @@ 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: |
@@ -144,14 +141,14 @@ jobs:
- name: Upload artifact
if: ${{ github.event.inputs.dry_run != 'true' }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz
path: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz
- name: Upload signature
if: ${{ github.event.inputs.dry_run != 'true' }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz.asc
path: ${{ matrix.build.binary }}-${{ needs.extract-version.outputs.VERSION }}-${{ matrix.configs.target }}.tar.gz.asc
@@ -173,7 +170,7 @@ jobs:
with:
fetch-depth: 0
- name: Download artifacts
uses: actions/download-artifact@v7
uses: actions/download-artifact@v6
- name: Generate full changelog
id: changelog
run: |

View File

@@ -42,7 +42,7 @@ jobs:
echo "Binaries SHA256 on ${{ matrix.machine }}: $(cat checksum.sha256)"
- name: Upload the hash
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v5
with:
name: checksum-${{ matrix.machine }}
path: |
@@ -55,12 +55,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download artifacts from machine-1
uses: actions/download-artifact@v7
uses: actions/download-artifact@v4
with:
name: checksum-machine-1
path: machine-1/
- name: Download artifacts from machine-2
uses: actions/download-artifact@v7
uses: actions/download-artifact@v4
with:
name: checksum-machine-2
path: machine-2/

View File

@@ -12,7 +12,6 @@ env:
CARGO_TERM_COLOR: always
FROM_BLOCK: 0
TO_BLOCK: 50000
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -23,7 +22,7 @@ jobs:
name: stage-run-test
# Only run stage commands test in merge groups
if: github.event_name == 'merge_group'
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
env:
RUST_LOG: info,sync=error
RUST_BACKTRACE: 1
@@ -32,7 +31,6 @@ 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,7 +9,6 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -18,7 +17,7 @@ concurrency:
jobs:
sync:
name: sync (${{ matrix.chain.bin }})
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
env:
RUST_LOG: info,sync=error
RUST_BACKTRACE: 1
@@ -42,7 +41,6 @@ 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,7 +9,6 @@ on:
env:
CARGO_TERM_COLOR: always
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -18,7 +17,7 @@ concurrency:
jobs:
sync:
name: sync (${{ matrix.chain.bin }})
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
env:
RUST_LOG: info,sync=error
RUST_BACKTRACE: 1
@@ -42,7 +41,6 @@ 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

@@ -11,7 +11,6 @@ on:
env:
CARGO_TERM_COLOR: always
SEED: rustethereumethereumrust
RUSTC_WRAPPER: "sccache"
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -20,7 +19,7 @@ concurrency:
jobs:
test:
name: test / ${{ matrix.type }} (${{ matrix.partition }}/${{ matrix.total_partitions }})
runs-on: depot-ubuntu-latest-4
runs-on: ubuntu-latest
env:
RUST_BACKTRACE: 1
strategy:
@@ -47,7 +46,6 @@ 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
@@ -66,7 +64,7 @@ jobs:
state:
name: Ethereum state tests
runs-on: depot-ubuntu-latest-4
runs-on: ubuntu-latest
env:
RUST_LOG: info,sync=error
RUST_BACKTRACE: 1
@@ -93,7 +91,6 @@ 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
@@ -101,7 +98,7 @@ jobs:
doc:
name: doc tests
runs-on: depot-ubuntu-latest
runs-on: ubuntu-latest
env:
RUST_BACKTRACE: 1
timeout-minutes: 30
@@ -109,7 +106,6 @@ 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

@@ -27,7 +27,7 @@ jobs:
./fetch_superchain_config.sh
- name: Create Pull Request
uses: peter-evans/create-pull-request@v8
uses: peter-evans/create-pull-request@v7
with:
commit-message: "chore: update superchain config"
title: "chore: update superchain config"

View File

@@ -9,12 +9,9 @@ on:
branches: [main]
merge_group:
env:
RUSTC_WRAPPER: "sccache"
jobs:
check-reth:
runs-on: depot-ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
@@ -24,7 +21,6 @@ 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
@@ -34,7 +30,7 @@ jobs:
run: cargo check --target x86_64-pc-windows-gnu
check-op-reth:
runs-on: depot-ubuntu-latest
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
@@ -44,7 +40,6 @@ 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

154
Cargo.lock generated
View File

@@ -97,9 +97,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
[[package]]
name = "alloy-chains"
version = "0.2.23"
version = "0.2.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35d744058a9daa51a8cf22a3009607498fcf82d3cf4c5444dd8056cdf651f471"
checksum = "1b9ebac8ff9c2f07667e1803dc777304337e160ce5153335beb45e8ec0751808"
dependencies = [
"alloy-primitives",
"alloy-rlp",
@@ -238,18 +238,6 @@ dependencies = [
"thiserror 2.0.17",
]
[[package]]
name = "alloy-eip7928"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "926b2c0d34e641cf8b17bf54ce50fda16715b9f68ad878fa6128bae410c6f890"
dependencies = [
"alloy-primitives",
"alloy-rlp",
"borsh",
"serde",
]
[[package]]
name = "alloy-eips"
version = "1.1.3"
@@ -278,9 +266,9 @@ dependencies = [
[[package]]
name = "alloy-evm"
version = "0.25.2"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6ccc4c702c840148af1ce784cc5c6ed9274a020ef32417c5b1dbeab8c317673"
checksum = "01be36ba6f5e6e62563b369e03ca529eac46aea50677f84655084b4750816574"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -329,9 +317,9 @@ dependencies = [
[[package]]
name = "alloy-json-abi"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bfca3dbbcb7498f0f60e67aff2ad6aff57032e22eb2fd03189854be11a22c03"
checksum = "5513d5e6bd1cba6bdcf5373470f559f320c05c8c59493b6e98912fbe6733943f"
dependencies = [
"alloy-primitives",
"alloy-sol-type-parser",
@@ -395,9 +383,9 @@ dependencies = [
[[package]]
name = "alloy-op-evm"
version = "0.25.2"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f640da852f93ddaa3b9a602b7ca41d80e0023f77a67b68aaaf511c32f1fe0ce"
checksum = "231262d7e06000f3fb642d32d38ca75e09e78e04977c10be0a07a5ee2c869cfd"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -426,9 +414,9 @@ dependencies = [
[[package]]
name = "alloy-primitives"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c850e6ccbd34b8a463a1e934ffc8fc00e1efc5e5489f2ad82d7797949f3bd4e"
checksum = "355bf68a433e0fd7f7d33d5a9fc2583fde70bf5c530f63b80845f8da5505cf28"
dependencies = [
"alloy-rlp",
"arbitrary",
@@ -447,7 +435,6 @@ dependencies = [
"proptest",
"proptest-derive 0.6.0",
"rand 0.9.2",
"rapidhash",
"ruint",
"rustc-hash",
"serde",
@@ -782,9 +769,9 @@ dependencies = [
[[package]]
name = "alloy-sol-macro"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b2218e3aeb3ee665d117fdf188db0d5acfdc3f7b7502c827421cb78f26a2aec0"
checksum = "f3ce480400051b5217f19d6e9a82d9010cdde20f1ae9c00d53591e4a1afbb312"
dependencies = [
"alloy-sol-macro-expander",
"alloy-sol-macro-input",
@@ -796,9 +783,9 @@ dependencies = [
[[package]]
name = "alloy-sol-macro-expander"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b231cb8cc48e66dd1c6e11a1402f3ac86c3667cbc13a6969a0ac030ba7bb8c88"
checksum = "6d792e205ed3b72f795a8044c52877d2e6b6e9b1d13f431478121d8d4eaa9028"
dependencies = [
"alloy-sol-macro-input",
"const-hex",
@@ -814,9 +801,9 @@ dependencies = [
[[package]]
name = "alloy-sol-macro-input"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49a522d79929c1bf0152b07567a38f7eaed3ab149e53e7528afa78ff11994668"
checksum = "0bd1247a8f90b465ef3f1207627547ec16940c35597875cdc09c49d58b19693c"
dependencies = [
"const-hex",
"dunce",
@@ -830,9 +817,9 @@ dependencies = [
[[package]]
name = "alloy-sol-type-parser"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0475c459859c8d9428af6ff3736614655a57efda8cc435a3b8b4796fa5ac1dd0"
checksum = "954d1b2533b9b2c7959652df3076954ecb1122a28cc740aa84e7b0a49f6ac0a9"
dependencies = [
"serde",
"winnow",
@@ -840,9 +827,9 @@ dependencies = [
[[package]]
name = "alloy-sol-types"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35287d9d821d5f26011bcd8d9101340898f761c9933cf50fca689bb7ed62fdeb"
checksum = "70319350969a3af119da6fb3e9bddb1bce66c9ea933600cb297c8b1850ad2a3c"
dependencies = [
"alloy-json-abi",
"alloy-primitives",
@@ -1382,9 +1369,9 @@ dependencies = [
[[package]]
name = "async-compression"
version = "0.4.36"
version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37"
checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473"
dependencies = [
"compression-codecs",
"compression-core",
@@ -1542,9 +1529,9 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "base64ct"
version = "1.8.1"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e050f626429857a27ddccb31e0aca21356bfa709c04041aefddac081a8f068a"
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
[[package]]
name = "bech32"
@@ -2390,9 +2377,9 @@ dependencies = [
[[package]]
name = "compression-codecs"
version = "0.4.35"
version = "0.4.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2"
checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad"
dependencies = [
"brotli",
"compression-core",
@@ -3684,17 +3671,6 @@ 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"
@@ -4006,9 +3982,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",
@@ -5475,9 +5451,9 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "libp2p-identity"
version = "0.2.13"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0c7892c221730ba55f7196e98b0b8ba5e04b4155651736036628e9f73ed6fc3"
checksum = "3104e13b51e4711ff5738caa1fb54467c8604c2e94d607e27745bcf709068774"
dependencies = [
"asn1_der",
"bs58",
@@ -6212,9 +6188,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
[[package]]
name = "op-alloy"
version = "0.23.1"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9b8fee21003dd4f076563de9b9d26f8c97840157ef78593cd7f262c5ca99848"
checksum = "c3b13412d297c1f9341f678b763750b120a73ffe998fa54a94d6eda98449e7ca"
dependencies = [
"op-alloy-consensus",
"op-alloy-network",
@@ -6225,9 +6201,9 @@ dependencies = [
[[package]]
name = "op-alloy-consensus"
version = "0.23.1"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "736381a95471d23e267263cfcee9e1d96d30b9754a94a2819148f83379de8a86"
checksum = "726da827358a547be9f1e37c2a756b9e3729cb0350f43408164794b370cad8ae"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -6251,9 +6227,9 @@ checksum = "a79f352fc3893dcd670172e615afef993a41798a1d3fc0db88a3e60ef2e70ecc"
[[package]]
name = "op-alloy-network"
version = "0.23.1"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4034183dca6bff6632e7c24c92e75ff5f0eabb58144edb4d8241814851334d47"
checksum = "f63f27e65be273ec8fcb0b6af0fd850b550979465ab93423705ceb3dfddbd2ab"
dependencies = [
"alloy-consensus",
"alloy-network",
@@ -6267,9 +6243,9 @@ dependencies = [
[[package]]
name = "op-alloy-provider"
version = "0.23.1"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6753d90efbaa8ea8bcb89c1737408ca85fa60d7adb875049d3f382c063666f86"
checksum = "a71456699aa256dc20119736422ad9a44da8b9585036117afb936778122093b9"
dependencies = [
"alloy-network",
"alloy-primitives",
@@ -6282,9 +6258,9 @@ dependencies = [
[[package]]
name = "op-alloy-rpc-jsonrpsee"
version = "0.23.1"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1c820ef9c802ebc732281a940bfb6ac2345af4d9fff041cbb64b4b546676686"
checksum = "8ef9114426b16172254555aad34a8ea96c01895e40da92f5d12ea680a1baeaa7"
dependencies = [
"alloy-primitives",
"jsonrpsee",
@@ -6292,9 +6268,9 @@ dependencies = [
[[package]]
name = "op-alloy-rpc-types"
version = "0.23.1"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ddd87c6b9e5b6eee8d6b76f41b04368dca0e9f38d83338e5b00e730c282098a4"
checksum = "562dd4462562c41f9fdc4d860858c40e14a25df7f983ae82047f15f08fce4d19"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -6312,9 +6288,9 @@ dependencies = [
[[package]]
name = "op-alloy-rpc-types-engine"
version = "0.23.1"
version = "0.22.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77727699310a18cdeed32da3928c709e2704043b6584ed416397d5da65694efc"
checksum = "d8f24b8cb66e4b33e6c9e508bf46b8ecafc92eadd0b93fedd306c0accb477657"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -6328,7 +6304,6 @@ dependencies = [
"ethereum_ssz_derive",
"op-alloy-consensus",
"serde",
"sha2",
"snap",
"thiserror 2.0.17",
]
@@ -7210,16 +7185,6 @@ dependencies = [
"rand_core 0.9.3",
]
[[package]]
name = "rapidhash"
version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8e65c75143ce5d47c55b510297eeb1182f3c739b6043c537670e9fc18612dae"
dependencies = [
"rand 0.9.2",
"rustversion",
]
[[package]]
name = "ratatui"
version = "0.29.0"
@@ -7374,9 +7339,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
[[package]]
name = "reqwest"
version = "0.12.25"
version = "0.12.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6eff9328d40131d43bd911d42d79eb6a47312002a4daefc9e37f17e74a7701a"
checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f"
dependencies = [
"base64 0.22.1",
"bytes",
@@ -8231,7 +8196,6 @@ name = "reth-engine-tree"
version = "1.9.3"
dependencies = [
"alloy-consensus",
"alloy-eip7928",
"alloy-eips",
"alloy-evm",
"alloy-primitives",
@@ -8650,7 +8614,6 @@ dependencies = [
"derive_more",
"futures-util",
"metrics",
"rayon",
"reth-ethereum-forks",
"reth-ethereum-primitives",
"reth-execution-errors",
@@ -8956,7 +8919,6 @@ dependencies = [
"pin-project",
"rand 0.8.5",
"rand 0.9.2",
"rayon",
"reth-chainspec",
"reth-consensus",
"reth-discv4",
@@ -9258,7 +9220,6 @@ dependencies = [
"alloy-sol-types",
"eyre",
"futures",
"jsonrpsee-core",
"rand 0.9.2",
"reth-chainspec",
"reth-db",
@@ -9294,7 +9255,6 @@ dependencies = [
"serde",
"serde_json",
"similar-asserts",
"tempfile",
"tokio",
]
@@ -10185,7 +10145,6 @@ dependencies = [
"reth-db-api",
"reth-engine-primitives",
"reth-errors",
"reth-ethereum-engine-primitives",
"reth-ethereum-primitives",
"reth-evm",
"reth-evm-ethereum",
@@ -10248,9 +10207,6 @@ dependencies = [
"reth-network-peers",
"reth-rpc-eth-api",
"reth-trie-common",
"serde",
"serde_json",
"tokio",
]
[[package]]
@@ -10315,7 +10271,6 @@ dependencies = [
"reth-rpc-server-types",
"reth-storage-api",
"reth-tasks",
"reth-tokio-util",
"reth-tracing",
"reth-transaction-pool",
"serde",
@@ -10907,7 +10862,6 @@ dependencies = [
"pretty_assertions",
"proptest",
"proptest-arbitrary-interop",
"rand 0.9.2",
"reth-ethereum-primitives",
"reth-execution-errors",
"reth-metrics",
@@ -11210,9 +11164,9 @@ dependencies = [
[[package]]
name = "revm-inspectors"
version = "0.33.2"
version = "0.33.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "01def7351cd9af844150b8e88980bcd11304f33ce23c3d7c25f2a8dab87c1345"
checksum = "6c93974333e7acc4b2dc024b10def99707f7375a4d53db7a7f8351722d25673f"
dependencies = [
"alloy-primitives",
"alloy-rpc-types-eth",
@@ -12094,9 +12048,9 @@ dependencies = [
[[package]]
name = "simd-adler32"
version = "0.3.8"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
[[package]]
name = "similar"
@@ -12351,9 +12305,9 @@ dependencies = [
[[package]]
name = "syn-solidity"
version = "1.5.0"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60ceeb7c95a4536de0c0e1649bd98d1a72a4bb9590b1f3e45a8a0bfdb7c188c0"
checksum = "ff790eb176cc81bb8936aed0f7b9f14fc4670069a2d371b3e3b0ecce908b2cb3"
dependencies = [
"paste",
"proc-macro2",
@@ -12918,9 +12872,9 @@ dependencies = [
[[package]]
name = "tower-http"
version = "0.6.8"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456"
dependencies = [
"async-compression",
"base64 0.22.1",

View File

@@ -153,7 +153,6 @@ 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",
@@ -376,11 +375,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" }
@@ -481,18 +480,17 @@ revm-primitives = { version = "21.0.2", default-features = false }
revm-interpreter = { version = "31.1.0", default-features = false }
revm-database-interface = { version = "8.0.5", default-features = false }
op-revm = { version = "14.1.0", default-features = false }
revm-inspectors = "0.33.2"
revm-inspectors = "0.33.1"
# 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-eip7928 = { version = "0.1.0" }
alloy-evm = { version = "0.25.1", default-features = false }
alloy-primitives = { version = "1.5.0", default-features = false, features = ["map-foldhash"] }
alloy-evm = { version = "0.24.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.5.0"
alloy-sol-types = { version = "1.5.0", default-features = false }
alloy-sol-macro = "1.4.1"
alloy-sol-types = { version = "1.4.1", default-features = false }
alloy-trie = { version = "0.9.1", default-features = false }
alloy-hardforks = "0.4.5"
@@ -526,13 +524,13 @@ 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.25.0", default-features = false }
alloy-op-evm = { version = "0.24.1", default-features = false }
alloy-op-hardforks = "0.4.4"
op-alloy-rpc-types = { version = "0.23.1", default-features = false }
op-alloy-rpc-types-engine = { version = "0.23.1", default-features = false }
op-alloy-network = { version = "0.23.1", default-features = false }
op-alloy-consensus = { version = "0.23.1", default-features = false }
op-alloy-rpc-jsonrpsee = { version = "0.23.1", default-features = false }
op-alloy-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-flz = { version = "0.13.1", default-features = false }
# misc

View File

@@ -521,3 +521,10 @@ pr:
make update-book-cli && \
cargo docs --document-private-items && \
make test
check-features:
cargo hack check \
--package reth-codecs \
--package reth-primitives-traits \
--package reth-primitives \
--feature-powerset

View File

@@ -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 (command is not stored for warmup phase)
let (mut node_process, _warmup_command) =
// Start reth node for warmup
let mut node_process =
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 and capture the command for reporting
let (mut node_process, reth_command) =
// Start reth node
let mut node_process =
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,9 +645,8 @@ async fn run_benchmark_workflow(
// Store results for comparison
comparison_generator.add_ref_results(ref_type, &output_dir)?;
// Set the benchmark run timestamps and reth command
// Set the benchmark run timestamps
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,8 +21,6 @@ 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
@@ -59,6 +57,7 @@ pub(crate) struct TotalGasRow {
/// - `mean_new_payload_latency_ms`: arithmetic mean latency across blocks.
/// - `median_new_payload_latency_ms`: p50 latency across blocks.
/// - `p90_new_payload_latency_ms` / `p99_new_payload_latency_ms`: tail latencies across blocks.
/// - `std_dev_new_payload_latency_ms`: standard deviation of latency across blocks.
#[derive(Debug, Clone, Serialize)]
pub(crate) struct BenchmarkSummary {
pub total_blocks: u64,
@@ -68,6 +67,7 @@ pub(crate) struct BenchmarkSummary {
pub median_new_payload_latency_ms: f64,
pub p90_new_payload_latency_ms: f64,
pub p99_new_payload_latency_ms: f64,
pub std_dev_new_payload_latency_ms: f64,
pub gas_per_second: f64,
pub blocks_per_second: f64,
pub min_block_number: u64,
@@ -91,7 +91,6 @@ 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.
@@ -99,6 +98,7 @@ pub(crate) struct RefInfo {
/// Percent deltas are `(feature - baseline) / baseline * 100`:
/// - `new_payload_latency_p50_change_percent` / p90 / p99: percent changes of the respective
/// per-block percentiles.
/// - `std_dev_change_percent`: percent change in standard deviation of newPayload latency.
/// - `per_block_latency_change_mean_percent` / `per_block_latency_change_median_percent` are the
/// mean and median of per-block percent deltas (feature vs baseline), capturing block-level
/// drift.
@@ -117,6 +117,7 @@ pub(crate) struct ComparisonSummary {
pub new_payload_latency_p50_change_percent: f64,
pub new_payload_latency_p90_change_percent: f64,
pub new_payload_latency_p99_change_percent: f64,
pub std_dev_change_percent: f64,
pub gas_per_second_change_percent: f64,
pub blocks_per_second_change_percent: f64,
}
@@ -145,8 +146,6 @@ impl ComparisonGenerator {
feature_ref_name: args.feature_ref.clone(),
baseline_results: None,
feature_results: None,
baseline_command: None,
feature_command: None,
}
}
@@ -211,21 +210,6 @@ 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...");
@@ -250,14 +234,12 @@ 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,
@@ -357,6 +339,9 @@ impl ComparisonGenerator {
let mean_new_payload_latency_ms: f64 =
latencies_ms.iter().sum::<f64>() / total_blocks as f64;
let std_dev_new_payload_latency_ms =
calculate_std_dev(&latencies_ms, mean_new_payload_latency_ms);
let mut sorted_latencies_ms = latencies_ms;
sorted_latencies_ms.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
let median_new_payload_latency_ms = percentile(&sorted_latencies_ms, 0.5);
@@ -387,6 +372,7 @@ impl ComparisonGenerator {
median_new_payload_latency_ms,
p90_new_payload_latency_ms,
p99_new_payload_latency_ms,
std_dev_new_payload_latency_ms,
gas_per_second,
blocks_per_second,
min_block_number,
@@ -454,6 +440,10 @@ impl ComparisonGenerator {
baseline.p99_new_payload_latency_ms,
feature.p99_new_payload_latency_ms,
),
std_dev_change_percent: calc_percent_change(
baseline.std_dev_new_payload_latency_ms,
feature.std_dev_new_payload_latency_ms,
),
gas_per_second_change_percent: calc_percent_change(
baseline.gas_per_second,
feature.gas_per_second,
@@ -584,6 +574,7 @@ impl ComparisonGenerator {
" NewPayload Latency p99: {:+.2}%",
summary.new_payload_latency_p99_change_percent
);
println!(" NewPayload Latency std dev: {:+.2}%", summary.std_dev_change_percent);
println!(
" Gas/Second: {:+.2}%",
summary.gas_per_second_change_percent
@@ -606,11 +597,12 @@ impl ComparisonGenerator {
);
println!(" NewPayload latency (ms):");
println!(
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}",
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}, std dev: {:.2}",
baseline.mean_new_payload_latency_ms,
baseline.median_new_payload_latency_ms,
baseline.p90_new_payload_latency_ms,
baseline.p99_new_payload_latency_ms
baseline.p99_new_payload_latency_ms,
baseline.std_dev_new_payload_latency_ms
);
if let (Some(start), Some(end)) =
(&report.baseline.start_timestamp, &report.baseline.end_timestamp)
@@ -621,9 +613,6 @@ 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:");
@@ -638,11 +627,12 @@ impl ComparisonGenerator {
);
println!(" NewPayload latency (ms):");
println!(
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}",
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}, std dev: {:.2}",
feature.mean_new_payload_latency_ms,
feature.median_new_payload_latency_ms,
feature.p90_new_payload_latency_ms,
feature.p99_new_payload_latency_ms
feature.p99_new_payload_latency_ms,
feature.std_dev_new_payload_latency_ms
);
if let (Some(start), Some(end)) =
(&report.feature.start_timestamp, &report.feature.end_timestamp)
@@ -653,9 +643,6 @@ 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,24 +240,19 @@ 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, String)> {
) -> Result<tokio::process::Child> {
// 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);
@@ -351,7 +346,7 @@ impl NodeManager {
// Give the node a moment to start up
sleep(Duration::from_secs(5)).await;
Ok((child, reth_command))
Ok(child)
}
/// 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.jwtsecret <jwt_file_path>
samply record -p 3001 target/profiling/reth node --metrics localhost:9001 --authrpc.jwt-secret <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 `--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 `--benchmark.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

@@ -81,7 +81,7 @@ backon.workspace = true
tempfile.workspace = true
[features]
default = ["jemalloc", "otlp", "reth-revm/portable", "js-tracer", "keccak-cache-global"]
default = ["jemalloc", "otlp", "reth-revm/portable", "js-tracer"]
otlp = [
"reth-ethereum-cli/otlp",
@@ -102,9 +102,7 @@ asm-keccak = [
"reth-ethereum-cli/asm-keccak",
"reth-node-ethereum/asm-keccak",
]
keccak-cache-global = [
"reth-node-ethereum/keccak-cache-global",
]
jemalloc = [
"reth-cli-util/jemalloc",
"reth-node-core/jemalloc",

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, ops::Deref, sync::Arc, time::Instant};
use std::{collections::BTreeMap, sync::Arc, time::Instant};
use tokio::sync::{broadcast, watch};
/// Size of the broadcast channel used to notify canonical state events.
@@ -634,8 +634,6 @@ 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();
@@ -648,22 +646,6 @@ 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

@@ -23,7 +23,7 @@ use reth_node_core::{
dirs::{ChainPath, DataDirPath},
};
use reth_provider::{
providers::{BlockchainProvider, NodeTypesForProvider, RocksDBProvider, StaticFileProvider},
providers::{BlockchainProvider, NodeTypesForProvider, StaticFileProvider},
ProviderFactory, StaticFileProviderFactory,
};
use reth_stages::{sets::DefaultStages, Pipeline, PipelineTarget};
@@ -75,12 +75,10 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
let data_dir = self.datadir.clone().resolve_datadir(self.chain.chain());
let db_path = data_dir.db();
let sf_path = data_dir.static_files();
let rocksdb_path = data_dir.rocksdb();
if access.is_read_write() {
reth_fs_util::create_dir_all(&db_path)?;
reth_fs_util::create_dir_all(&sf_path)?;
reth_fs_util::create_dir_all(&rocksdb_path)?;
}
let config_path = self.config.clone().unwrap_or_else(|| data_dir.config());
@@ -110,14 +108,8 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
StaticFileProvider::read_only(sf_path, false)?,
),
};
// TransactionDB only support read-write mode
let rocksdb_provider = RocksDBProvider::builder(data_dir.rocksdb())
.with_default_tables()
.with_database_log_level(self.db.log_level)
.build()?;
let provider_factory =
self.create_provider_factory(&config, db, sfp, rocksdb_provider, access)?;
let provider_factory = self.create_provider_factory(&config, db, sfp, access)?;
if access.is_read_write() {
debug!(target: "reth::cli", chain=%self.chain.chain(), genesis=?self.chain.genesis_hash(), "Initializing genesis");
init_genesis_with_settings(&provider_factory, self.static_files.to_settings())?;
@@ -136,7 +128,6 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
config: &Config,
db: Arc<DatabaseEnv>,
static_file_provider: StaticFileProvider<N::Primitives>,
rocksdb_provider: RocksDBProvider,
access: AccessRights,
) -> eyre::Result<ProviderFactory<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>>
where
@@ -147,7 +138,6 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
db,
self.chain.clone(),
static_file_provider,
rocksdb_provider,
)?
.with_prune_modes(prune_modes.clone());

View File

@@ -8,17 +8,12 @@ use reth_db::{
RawDupSort,
};
use reth_db_api::{
cursor::{DbCursorRO, DbDupCursorRO},
database::Database,
table::{Compress, Decompress, DupSort, Table},
tables,
transaction::DbTx,
RawKey, RawTable, Receipts, TableViewer, Transactions,
table::{Decompress, DupSort, Table},
tables, 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;
@@ -44,14 +39,6 @@ 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,
@@ -74,8 +61,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, end_key, end_subkey, raw } => {
table.view(&GetValueViewer { tool, key, subkey, end_key, end_subkey, raw })?
Subcommand::Mdbx { table, key, subkey, raw } => {
table.view(&GetValueViewer { tool, key, subkey, raw })?
}
Subcommand::StaticFile { segment, key, raw } => {
let (key, mask): (u64, _) = match segment {
@@ -167,8 +154,6 @@ struct GetValueViewer<'a, N: NodeTypesWithDB> {
tool: &'a DbTool<N>,
key: String,
subkey: Option<String>,
end_key: Option<String>,
end_subkey: Option<String>,
raw: bool,
}
@@ -178,158 +163,53 @@ impl<N: ProviderNodeTypes> TableViewer<()> for GetValueViewer<'_, N> {
fn view<T: Table>(&self) -> Result<(), Self::Error> {
let key = table_key::<T>(&self.key)?;
// 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"));
}
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>(())
})??;
let content = if self.raw {
self.tool
.get::<RawTable<T>>(RawKey::from(key))?
.map(|content| hex::encode_prefixed(content.raw_value()))
} 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()?
};
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.");
}
};
}
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>
where
T::Value: reth_primitives_traits::ValueWithSubKey<SubKey = T::SubKey>,
{
fn view_dupsort<T: DupSort>(&self) -> Result<(), Self::Error> {
// get a key for given table
let key = table_key::<T>(&self.key)?;
// 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(|s| table_subkey::<T>(Some(s.as_str())))
.transpose()?;
// process dupsort table
let subkey = table_subkey::<T>(self.subkey.as_deref())?;
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>(())
})??;
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 {
// Single key/subkey lookup
let subkey = table_subkey::<T>(self.subkey.as_deref())?;
self.tool
.get_dup::<T>(key, subkey)?
.as_ref()
.map(serde_json::to_string_pretty)
.transpose()?
};
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.");
}
};
}
match content {
Some(content) => {
println!("{content}");
}
None => {
error!(target: "reth::cli", "No content for the given table subkey.");
}
};
Ok(())
}
}

View File

@@ -2,7 +2,6 @@ use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
use clap::{Parser, Subcommand};
use reth_chainspec::{EthChainSpec, EthereumHardforks};
use reth_cli::chainspec::ChainSpecParser;
use reth_cli_runner::CliContext;
use reth_db::version::{get_db_version, DatabaseVersionError, DB_VERSION};
use reth_db_common::DbTool;
use std::{
@@ -80,10 +79,7 @@ macro_rules! db_exec {
impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C> {
/// Execute `db` command
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(
self,
ctx: CliContext,
) -> eyre::Result<()> {
pub async fn execute<N: CliNodeTypes<ChainSpec = C::ChainSpec>>(self) -> eyre::Result<()> {
let data_dir = self.env.datadir.clone().resolve_datadir(self.env.chain.chain());
let db_path = data_dir.db();
let static_files_path = data_dir.static_files();
@@ -162,7 +158,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C>
let access_rights =
if command.dry_run { AccessRights::RO } else { AccessRights::RW };
db_exec!(self.env, tool, N, access_rights, {
command.execute(&tool, ctx.task_executor.clone())?;
command.execute(&tool)?;
});
}
Subcommands::StaticFileHeader(command) => {

View File

@@ -18,7 +18,6 @@ use reth_node_metrics::{
};
use reth_provider::{providers::ProviderNodeTypes, ChainSpecProvider, StageCheckpointReader};
use reth_stages::StageId;
use reth_tasks::TaskExecutor;
use reth_trie::{
verify::{Output, Verifier},
Nibbles,
@@ -49,37 +48,52 @@ pub struct Command {
impl Command {
/// Execute `db repair-trie` command
pub fn execute<N: ProviderNodeTypes>(
self,
tool: &DbTool<N>,
task_executor: TaskExecutor,
) -> eyre::Result<()> {
pub fn execute<N: ProviderNodeTypes>(self, tool: &DbTool<N>) -> eyre::Result<()> {
// Set up metrics server if requested
let _metrics_handle = if let Some(listen_addr) = self.metrics {
// Spawn an OS thread with a single-threaded tokio runtime for the metrics server
let chain_name = tool.provider_factory.chain_spec().chain().to_string();
let executor = task_executor.clone();
let handle = task_executor.spawn_critical("metrics server", async move {
let config = MetricServerConfig::new(
listen_addr,
VersionInfo {
version: version_metadata().cargo_pkg_version.as_ref(),
build_timestamp: version_metadata().vergen_build_timestamp.as_ref(),
cargo_features: version_metadata().vergen_cargo_features.as_ref(),
git_sha: version_metadata().vergen_git_sha.as_ref(),
target_triple: version_metadata().vergen_cargo_target_triple.as_ref(),
build_profile: version_metadata().build_profile_name.as_ref(),
},
ChainSpecInfo { name: chain_name },
executor,
Hooks::builder().build(),
);
let handle = std::thread::Builder::new().name("metrics-server".to_string()).spawn(
move || {
// Create a single-threaded tokio runtime
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.expect("Failed to create tokio runtime for metrics server");
// Spawn the metrics server
if let Err(e) = MetricServer::new(config).serve().await {
tracing::error!("Metrics server error: {}", e);
}
});
let handle = runtime.handle().clone();
runtime.block_on(async move {
let task_manager = reth_tasks::TaskManager::new(handle.clone());
let task_executor = task_manager.executor();
let config = MetricServerConfig::new(
listen_addr,
VersionInfo {
version: version_metadata().cargo_pkg_version.as_ref(),
build_timestamp: version_metadata().vergen_build_timestamp.as_ref(),
cargo_features: version_metadata().vergen_cargo_features.as_ref(),
git_sha: version_metadata().vergen_git_sha.as_ref(),
target_triple: version_metadata()
.vergen_cargo_target_triple
.as_ref(),
build_profile: version_metadata().build_profile_name.as_ref(),
},
ChainSpecInfo { name: chain_name },
task_executor,
Hooks::builder().build(),
);
// Spawn the metrics server
if let Err(e) = MetricServer::new(config).serve().await {
tracing::error!("Metrics server error: {}", e);
}
// Block forever to keep the runtime alive
std::future::pending::<()>().await
});
},
)?;
Some(handle)
} else {

View File

@@ -92,8 +92,6 @@ impl Command {
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."
"Data directory should be empty when calling init-state with --without-evm-history."
));
}
}

View File

@@ -189,7 +189,7 @@ impl<C: ChainSpecParser> DownloadArgs<C> {
let net = NetworkConfigBuilder::<N::NetworkPrimitives>::new(p2p_secret_key)
.peer_config(config.peers_config_with_basic_nodes_from_file(None))
.external_ip_resolver(self.network.nat.clone())
.external_ip_resolver(self.network.nat)
.network_id(self.network.network_id)
.boot_nodes(boot_nodes.clone())
.apply(|builder| {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -531,12 +531,8 @@ impl PruneConfig {
self.segments.receipts.is_some() || !self.segments.receipts_log_filter.is_empty()
}
/// 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.
/// 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.
pub fn merge(&mut self, other: Self) {
let Self {
block_interval,
@@ -565,7 +561,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; always take the value from `other`
// Merkle changesets is not optional, so we just replace it if provided
self.segments.merkle_changesets = merkle_changesets;
if self.segments.receipts_log_filter.0.is_empty() && !receipts_log_filter.0.is_empty() {

View File

@@ -279,28 +279,20 @@ pub fn validate_against_parent_hash_number<H: BlockHeader>(
header: &H,
parent: &SealedHeader<H>,
) -> Result<(), ConsensusError> {
if parent.hash() != header.parent_hash() {
return Err(ConsensusError::ParentHashMismatch(
GotExpected { got: header.parent_hash(), expected: parent.hash() }.into(),
))
}
let Some(parent_number) = parent.number().checked_add(1) else {
// parent block already reached the maximum
return Err(ConsensusError::ParentBlockNumberMismatch {
parent_block_number: parent.number(),
block_number: u64::MAX,
})
};
// Parent number is consistent.
if parent_number != header.number() {
if parent.number() + 1 != header.number() {
return Err(ConsensusError::ParentBlockNumberMismatch {
parent_block_number: parent.number(),
block_number: header.number(),
})
}
if parent.hash() != header.parent_hash() {
return Err(ConsensusError::ParentHashMismatch(
GotExpected { got: header.parent_hash(), expected: parent.hash() }.into(),
))
}
Ok(())
}
@@ -335,7 +327,7 @@ pub fn validate_against_parent_eip1559_base_fee<ChainSpec: EthChainSpec + Ethere
Ok(())
}
/// Validates that the block timestamp is greater than the parent block timestamp.
/// Validates the timestamp against the parent to make sure it is in the past.
#[inline]
pub fn validate_against_parent_timestamp<H: BlockHeader>(
header: &H,

View File

@@ -1,5 +1,5 @@
use crate::BlockProvider;
use alloy_provider::{ConnectionConfig, Network, Provider, ProviderBuilder, WebSocketConfig};
use alloy_provider::{Network, Provider, ProviderBuilder};
use alloy_transport::TransportResult;
use futures::{Stream, StreamExt};
use reth_node_api::Block;
@@ -25,19 +25,7 @@ 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_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?,
),
provider: Arc::new(ProviderBuilder::default().connect(rpc_url).await?),
url: rpc_url.to_string(),
convert: Arc::new(convert),
})

View File

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

View File

@@ -1,7 +1,5 @@
//! 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;
@@ -42,7 +40,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 = EPOCH_SLOTS as u32 * 2;
const DEFAULT_BLOCK_BUFFER_LIMIT: u32 = 256;
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;
@@ -91,8 +89,6 @@ 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.
@@ -101,7 +97,7 @@ pub struct TreeConfig {
state_provider_metrics: bool,
/// Cross-block cache size in bytes.
cross_block_cache_size: u64,
/// Whether the host has enough parallelism to run state root task.
/// Whether the host has enough parallelism to run state root in parallel.
has_enough_parallelism: bool,
/// Whether multiproof task should chunk proof targets.
multiproof_chunking_enabled: bool,
@@ -147,7 +143,6 @@ 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,
@@ -178,7 +173,6 @@ 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,
@@ -203,7 +197,6 @@ 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,
@@ -278,12 +271,7 @@ impl TreeConfig {
self.disable_parallel_sparse_trie
}
/// 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.
/// Returns whether or not parallel prewarming should be used.
pub const fn disable_prewarming(&self) -> bool {
self.disable_prewarming
}
@@ -375,12 +363,6 @@ 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;
@@ -403,12 +385,17 @@ impl TreeConfig {
self
}
/// Setter for has enough parallelism.
/// Setter for whether or not the host has enough parallelism to run state root in parallel.
pub const fn with_has_enough_parallelism(mut self, has_enough_parallelism: bool) -> Self {
self.has_enough_parallelism = has_enough_parallelism;
self
}
/// Whether or not the host has enough parallelism to run state root in parallel.
pub const fn has_enough_parallelism(&self) -> bool {
self.has_enough_parallelism
}
/// Setter for state provider metrics.
pub const fn with_state_provider_metrics(mut self, state_provider_metrics: bool) -> Self {
self.state_provider_metrics = state_provider_metrics;

View File

@@ -22,8 +22,7 @@ use reth_trie_common::HashedPostState;
use serde::{de::DeserializeOwned, Serialize};
// Re-export [`ExecutionPayload`] moved to `reth_payload_primitives`
#[cfg(feature = "std")]
pub use reth_evm::{ConfigureEngineEvm, ExecutableTxIterator, ExecutableTxTuple};
pub use reth_evm::{ConfigureEngineEvm, ExecutableTxIterator};
pub use reth_payload_primitives::ExecutionPayload;
mod error;

View File

@@ -16,7 +16,7 @@ reth-chain-state.workspace = true
reth-chainspec = { workspace = true, optional = true }
reth-consensus.workspace = true
reth-db.workspace = true
reth-engine-primitives = { workspace = true, features = ["std"] }
reth-engine-primitives.workspace = true
reth-errors.workspace = true
reth-execution-types.workspace = true
reth-evm = { workspace = true, features = ["metrics"] }
@@ -39,7 +39,6 @@ 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

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

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(Debug, Default)]
#[derive(Default)]
pub(crate) struct AtomicDuration {
/// The nanoseconds part of the duration
///
@@ -59,8 +59,7 @@ impl AtomicDuration {
}
/// A wrapper of a state provider and latency metrics.
#[derive(Debug)]
pub struct InstrumentedStateProvider<S> {
pub(crate) struct InstrumentedStateProvider<S> {
/// The state provider
state_provider: S,
@@ -81,12 +80,11 @@ impl<S> InstrumentedStateProvider<S>
where
S: StateProvider,
{
/// 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 {
/// Creates a new [`InstrumentedStateProvider`] from a state provider
pub(crate) fn from_state_provider(state_provider: S) -> Self {
Self {
state_provider,
metrics: StateProviderMetrics::new_with_labels(&[("source", source)]),
metrics: StateProviderMetrics::default(),
total_storage_fetch_latency: AtomicDuration::zero(),
total_code_fetch_latency: AtomicDuration::zero(),
total_account_fetch_latency: AtomicDuration::zero(),
@@ -136,12 +134,6 @@ 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,
mut transactions: impl Iterator<Item = Result<impl ExecutableTx<E>, BlockExecutionError>>,
transactions: impl Iterator<Item = Result<impl ExecutableTx<E>, BlockExecutionError>>,
state_hook: Box<dyn OnStateHook>,
) -> Result<(BlockExecutionOutput<E::Receipt>, Vec<Address>), BlockExecutionError>
where
@@ -79,42 +79,27 @@ 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();
loop {
let start = Instant::now();
let Some(tx) = transactions.next() else { break };
self.executor.transaction_wait_histogram.record(start.elapsed());
for tx in transactions {
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");
let start = Instant::now();
senders.push(*tx.signer());
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);
let start = Instant::now();
let result = debug_span!(target: "engine::tree", "finish")
debug_span!(target: "engine::tree", "finish")
.entered()
.in_scope(|| executor.finish())
.map(|(evm, result)| (evm.into_db(), result));
self.executor.post_execution_histogram.record(start.elapsed());
result
.map(|(evm, result)| (evm.into_db(), 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;
pub mod instrumented_state;
mod instrumented_state;
mod invalid_headers;
mod metrics;
mod payload_processor;

View File

@@ -1,318 +0,0 @@
//! BAL (Block Access List, EIP-7928) related functionality.
use alloy_consensus::constants::KECCAK_EMPTY;
use alloy_eip7928::BlockAccessList;
use alloy_primitives::{keccak256, U256};
use reth_primitives_traits::Account;
use reth_provider::{AccountReader, ProviderError};
use reth_trie::{HashedPostState, HashedStorage};
/// Converts a Block Access List into a [`HashedPostState`] by extracting the final state
/// of modified accounts and storage slots.
pub fn bal_to_hashed_post_state<P>(
bal: &BlockAccessList,
provider: &P,
) -> Result<HashedPostState, ProviderError>
where
P: AccountReader,
{
let mut hashed_state = HashedPostState::with_capacity(bal.len());
for account_changes in bal {
let address = account_changes.address;
let hashed_address = keccak256(address);
// Get the latest balance (last balance change if any)
let balance = account_changes.balance_changes.last().map(|change| change.post_balance);
// Get the latest nonce (last nonce change if any)
let nonce = account_changes.nonce_changes.last().map(|change| change.new_nonce);
// Get the latest code (last code change if any)
let code_hash = if let Some(code_change) = account_changes.code_changes.last() {
if code_change.new_code.is_empty() {
Some(Some(KECCAK_EMPTY))
} else {
Some(Some(keccak256(&code_change.new_code)))
}
} else {
None
};
// Only fetch account from provider if we're missing any field
let existing_account = if balance.is_none() || nonce.is_none() || code_hash.is_none() {
provider.basic_account(&address)?
} else {
None
};
// Build the final account state
let account = Account {
balance: balance.unwrap_or_else(|| {
existing_account.as_ref().map(|acc| acc.balance).unwrap_or(U256::ZERO)
}),
nonce: nonce
.unwrap_or_else(|| existing_account.as_ref().map(|acc| acc.nonce).unwrap_or(0)),
bytecode_hash: code_hash.unwrap_or_else(|| {
existing_account.as_ref().and_then(|acc| acc.bytecode_hash).or(Some(KECCAK_EMPTY))
}),
};
hashed_state.accounts.insert(hashed_address, Some(account));
// Process storage changes
if !account_changes.storage_changes.is_empty() {
let mut storage_map = HashedStorage::new(false);
for slot_changes in &account_changes.storage_changes {
let hashed_slot = keccak256(slot_changes.slot);
// Get the last change for this slot
if let Some(last_change) = slot_changes.changes.last() {
storage_map
.storage
.insert(hashed_slot, U256::from_be_bytes(last_change.new_value.0));
}
}
if !storage_map.storage.is_empty() {
hashed_state.storages.insert(hashed_address, storage_map);
}
}
}
Ok(hashed_state)
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_eip7928::{
AccountChanges, BalanceChange, CodeChange, NonceChange, SlotChanges, StorageChange,
};
use alloy_primitives::{Address, Bytes, StorageKey, B256};
use reth_revm::test_utils::StateProviderTest;
#[test]
fn test_bal_to_hashed_post_state_basic() {
let provider = StateProviderTest::default();
let address = Address::random();
let account_changes = AccountChanges {
address,
storage_changes: vec![],
storage_reads: vec![],
balance_changes: vec![BalanceChange::new(0, U256::from(100))],
nonce_changes: vec![NonceChange::new(0, 1)],
code_changes: vec![],
};
let bal = vec![account_changes];
let result = bal_to_hashed_post_state(&bal, &provider).unwrap();
assert_eq!(result.accounts.len(), 1);
let hashed_address = keccak256(address);
let account_opt = result.accounts.get(&hashed_address).unwrap();
assert!(account_opt.is_some());
let account = account_opt.as_ref().unwrap();
assert_eq!(account.balance, U256::from(100));
assert_eq!(account.nonce, 1);
assert_eq!(account.bytecode_hash, Some(KECCAK_EMPTY));
}
#[test]
fn test_bal_with_storage_changes() {
let provider = StateProviderTest::default();
let address = Address::random();
let slot = StorageKey::random();
let value = B256::random();
let slot_changes = SlotChanges { slot, changes: vec![StorageChange::new(0, value)] };
let account_changes = AccountChanges {
address,
storage_changes: vec![slot_changes],
storage_reads: vec![],
balance_changes: vec![BalanceChange::new(0, U256::from(500))],
nonce_changes: vec![NonceChange::new(0, 2)],
code_changes: vec![],
};
let bal = vec![account_changes];
let result = bal_to_hashed_post_state(&bal, &provider).unwrap();
let hashed_address = keccak256(address);
assert!(result.storages.contains_key(&hashed_address));
let storage = result.storages.get(&hashed_address).unwrap();
let hashed_slot = keccak256(slot);
let stored_value = storage.storage.get(&hashed_slot).unwrap();
assert_eq!(*stored_value, U256::from_be_bytes(value.0));
}
#[test]
fn test_bal_with_code_change() {
let provider = StateProviderTest::default();
let address = Address::random();
let code = Bytes::from(vec![0x60, 0x80, 0x60, 0x40]); // Some bytecode
let account_changes = AccountChanges {
address,
storage_changes: vec![],
storage_reads: vec![],
balance_changes: vec![BalanceChange::new(0, U256::from(1000))],
nonce_changes: vec![NonceChange::new(0, 1)],
code_changes: vec![CodeChange::new(0, code.clone())],
};
let bal = vec![account_changes];
let result = bal_to_hashed_post_state(&bal, &provider).unwrap();
let hashed_address = keccak256(address);
let account_opt = result.accounts.get(&hashed_address).unwrap();
let account = account_opt.as_ref().unwrap();
let expected_code_hash = keccak256(&code);
assert_eq!(account.bytecode_hash, Some(expected_code_hash));
}
#[test]
fn test_bal_with_empty_code() {
let provider = StateProviderTest::default();
let address = Address::random();
let empty_code = Bytes::default();
let account_changes = AccountChanges {
address,
storage_changes: vec![],
storage_reads: vec![],
balance_changes: vec![BalanceChange::new(0, U256::from(1000))],
nonce_changes: vec![NonceChange::new(0, 1)],
code_changes: vec![CodeChange::new(0, empty_code)],
};
let bal = vec![account_changes];
let result = bal_to_hashed_post_state(&bal, &provider).unwrap();
let hashed_address = keccak256(address);
let account_opt = result.accounts.get(&hashed_address).unwrap();
let account = account_opt.as_ref().unwrap();
assert_eq!(account.bytecode_hash, Some(KECCAK_EMPTY));
}
#[test]
fn test_bal_multiple_changes_takes_last() {
let provider = StateProviderTest::default();
let address = Address::random();
// Multiple balance changes - should take the last one
let account_changes = AccountChanges {
address,
storage_changes: vec![],
storage_reads: vec![],
balance_changes: vec![
BalanceChange::new(0, U256::from(100)),
BalanceChange::new(1, U256::from(200)),
BalanceChange::new(2, U256::from(300)),
],
nonce_changes: vec![
NonceChange::new(0, 1),
NonceChange::new(1, 2),
NonceChange::new(2, 3),
],
code_changes: vec![],
};
let bal = vec![account_changes];
let result = bal_to_hashed_post_state(&bal, &provider).unwrap();
let hashed_address = keccak256(address);
let account_opt = result.accounts.get(&hashed_address).unwrap();
let account = account_opt.as_ref().unwrap();
// Should have the last values
assert_eq!(account.balance, U256::from(300));
assert_eq!(account.nonce, 3);
}
#[test]
fn test_bal_uses_provider_for_missing_fields() {
let mut provider = StateProviderTest::default();
let address = Address::random();
let code_hash = B256::random();
let existing_account =
Account { balance: U256::from(999), nonce: 42, bytecode_hash: Some(code_hash) };
provider.insert_account(address, existing_account, None, Default::default());
// Only change balance, nonce and code should come from provider
let account_changes = AccountChanges {
address,
storage_changes: vec![],
storage_reads: vec![],
balance_changes: vec![BalanceChange::new(0, U256::from(1500))],
nonce_changes: vec![],
code_changes: vec![],
};
let bal = vec![account_changes];
let result = bal_to_hashed_post_state(&bal, &provider).unwrap();
let hashed_address = keccak256(address);
let account_opt = result.accounts.get(&hashed_address).unwrap();
let account = account_opt.as_ref().unwrap();
// Balance should be updated
assert_eq!(account.balance, U256::from(1500));
// Nonce and bytecode_hash should come from provider
assert_eq!(account.nonce, 42);
assert_eq!(account.bytecode_hash, Some(code_hash));
}
#[test]
fn test_bal_multiple_storage_changes_per_slot() {
let provider = StateProviderTest::default();
let address = Address::random();
let slot = StorageKey::random();
// Multiple changes to the same slot - should take the last one
let slot_changes = SlotChanges {
slot,
changes: vec![
StorageChange::new(0, B256::from(U256::from(100).to_be_bytes::<32>())),
StorageChange::new(1, B256::from(U256::from(200).to_be_bytes::<32>())),
StorageChange::new(2, B256::from(U256::from(300).to_be_bytes::<32>())),
],
};
let account_changes = AccountChanges {
address,
storage_changes: vec![slot_changes],
storage_reads: vec![],
balance_changes: vec![BalanceChange::new(0, U256::from(100))],
nonce_changes: vec![NonceChange::new(0, 1)],
code_changes: vec![],
};
let bal = vec![account_changes];
let result = bal_to_hashed_post_state(&bal, &provider).unwrap();
let hashed_address = keccak256(address);
let storage = result.storages.get(&hashed_address).unwrap();
let hashed_slot = keccak256(slot);
let stored_value = storage.storage.get(&hashed_slot).unwrap();
// Should have the last value
assert_eq!(*stored_value, U256::from(300));
}
}

View File

@@ -13,7 +13,6 @@ use crate::tree::{
sparse_trie::SparseTrieTask,
StateProviderBuilder, TreeConfig,
};
use alloy_eip7928::BlockAccessList;
use alloy_eips::eip1898::BlockWithParent;
use alloy_evm::{block::StateChangeSource, ToTxEnv};
use alloy_primitives::B256;
@@ -22,7 +21,7 @@ use executor::WorkloadExecutor;
use multiproof::{SparseTrieUpdate, *};
use parking_lot::RwLock;
use prewarm::PrewarmMetrics;
use rayon::prelude::*;
use rayon::iter::{ParallelBridge, ParallelIterator};
use reth_engine_primitives::ExecutableTxIterator;
use reth_evm::{
execute::{ExecutableTxFor, WithTxEnv},
@@ -50,9 +49,8 @@ use std::{
},
time::Instant,
};
use tracing::{debug, debug_span, error, instrument, warn, Span};
use tracing::{debug, debug_span, instrument, warn, Span};
pub mod bal;
mod configured_sparse_trie;
pub mod executor;
pub mod multiproof;
@@ -108,8 +106,6 @@ 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.
@@ -153,7 +149,6 @@ 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(),
@@ -214,7 +209,6 @@ where
provider_builder: StateProviderBuilder<N, P>,
multiproof_provider_factory: F,
config: &TreeConfig,
bal: Option<Arc<BlockAccessList>>,
) -> PayloadHandle<WithTxEnv<TxEnvFor<Evm>, I::Tx>, I::Error>
where
P: BlockReader + StateProviderFactory + StateReader + Clone + 'static,
@@ -255,45 +249,19 @@ where
// wire the multiproof task to the prewarm task
let to_multi_proof = Some(multi_proof_task.state_root_message_sender());
// Handle BAL-based optimization if available
let prewarm_handle = if let Some(bal) = bal {
// When BAL is present, skip spawning prewarm tasks entirely and send BAL to multiproof
debug!(target: "engine::tree::payload_processor", "BAL present, skipping prewarm tasks");
// Send BAL message immediately to MultiProofTask
if let Some(ref sender) = to_multi_proof &&
let Err(err) = sender.send(MultiProofMessage::BlockAccessList(bal))
{
// In this case state root validation will simply fail
error!(target: "engine::tree::payload_processor", ?err, "Failed to send BAL to MultiProofTask");
}
// Spawn minimal cache-only task without prewarming
self.spawn_caching_with(
env,
prewarm_rx,
transaction_count_hint,
provider_builder.clone(),
None, // Don't send proof targets when BAL is present
)
} else {
// Normal path: spawn with full prewarming
self.spawn_caching_with(
env,
prewarm_rx,
transaction_count_hint,
provider_builder.clone(),
to_multi_proof.clone(),
)
};
let prewarm_handle = self.spawn_caching_with(
env,
prewarm_rx,
transaction_count_hint,
provider_builder,
to_multi_proof.clone(),
);
// spawn multi-proof task
let parent_span = span.clone();
self.executor.spawn_blocking(move || {
let _enter = parent_span.entered();
// Build a state provider for the multiproof task
let provider = provider_builder.build().expect("failed to build provider");
multi_proof_task.run(provider);
multi_proof_task.run();
});
// wire the sparse trie to the state root response receiver
@@ -347,32 +315,36 @@ where
usize,
) {
let (transactions, convert) = transactions.into();
let transactions = transactions.into_par_iter();
let transaction_count_hint = transactions.len();
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);
let (ooo_tx, ooo_rx) = mpsc::channel();
let (prewarm_tx, prewarm_rx) = mpsc::channel();
let (execute_tx, execute_rx) = mpsc::channel();
// Spawn a task that `convert`s all transactions in parallel and sends them out-of-order.
// 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().for_each_with(ooo_tx, |ooo_tx, (idx, tx)| {
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) });
// Only send Ok(_) variants to prewarming task.
if let Ok(tx) = &tx {
let _ = prewarm_tx.send(tx.clone());
}
let _ = ooo_tx.send((idx, tx));
let _ = sender.send((idx, tx));
});
});
// Spawn a task that processes out-of-order transactions from the task above and sends them
// to the execution task in order.
// to prewarming and execution tasks.
let (prewarm_tx, prewarm_rx) = mpsc::channel();
let (execute_tx, execute_rx) = mpsc::channel();
self.executor.spawn_blocking(move || {
let mut next_for_execution = 0;
let mut queue = BTreeMap::new();
while let Ok((idx, tx)) = ooo_rx.recv() {
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());
}
if next_for_execution == idx {
let _ = execute_tx.send(tx);
next_for_execution += 1;
@@ -410,15 +382,9 @@ where
transactions = mpsc::channel().1;
}
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))
};
let saved_cache = self.cache_for(env.parent_hash);
let cache = saved_cache.cache().clone();
let cache_metrics = saved_cache.metrics().clone();
// configure prewarming
let prewarm_ctx = PrewarmContext {
env,
@@ -624,18 +590,18 @@ impl<Tx, Err> PayloadHandle<Tx, Err> {
move |source: StateChangeSource, state: &EvmState| {
if let Some(sender) = &to_multi_proof {
let _ = sender.send(MultiProofMessage::StateUpdate(source.into(), state.clone()));
let _ = sender.send(MultiProofMessage::StateUpdate(source, state.clone()));
}
}
}
/// Returns a clone of the caches used by prewarming
pub(super) fn caches(&self) -> Option<StateExecutionCache> {
pub(super) fn caches(&self) -> StateExecutionCache {
self.prewarm_handle.cache.clone()
}
/// Returns a clone of the cache metrics used by prewarming
pub(super) fn cache_metrics(&self) -> Option<CachedStateMetrics> {
pub(super) fn cache_metrics(&self) -> CachedStateMetrics {
self.prewarm_handle.cache_metrics.clone()
}
@@ -665,9 +631,9 @@ impl<Tx, Err> PayloadHandle<Tx, Err> {
#[derive(Debug)]
pub(crate) struct CacheTaskHandle {
/// The shared cache the task operates with.
cache: Option<StateExecutionCache>,
cache: StateExecutionCache,
/// Metrics for the caches
cache_metrics: Option<CachedStateMetrics>,
cache_metrics: CachedStateMetrics,
/// Channel to the spawned prewarm task if any
to_prewarm_task: Option<std::sync::mpsc::Sender<PrewarmTaskEvent>>,
}
@@ -1082,17 +1048,19 @@ mod tests {
let provider_factory = BlockchainProvider::new(factory).unwrap();
let mut handle = payload_processor.spawn(
Default::default(),
(
Vec::<Result<Recovered<TransactionSigned>, core::convert::Infallible>>::new(),
std::convert::identity,
),
StateProviderBuilder::new(provider_factory.clone(), genesis_hash, None),
OverlayStateProviderFactory::new(provider_factory),
&TreeConfig::default(),
None, // No BAL for test
);
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, StateProviderBox, StateProviderFactory, StateReader};
use reth_provider::{BlockReader, StateProviderFactory, StateReader};
use reth_revm::{database::StateProviderDatabase, db::BundleState, state::EvmState};
use reth_trie::MultiProofTargets;
use std::{
@@ -255,35 +255,31 @@ where
self;
let hash = env.hash;
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);
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.
@@ -360,7 +356,7 @@ where
{
pub(super) env: ExecutionEnv<Evm>,
pub(super) evm_config: Evm,
pub(super) saved_cache: Option<SavedCache>,
pub(super) saved_cache: SavedCache,
/// Provider to obtain the state
pub(super) provider: StateProviderBuilder<N, P>,
pub(super) metrics: PrewarmMetrics,
@@ -404,13 +400,10 @@ where
};
// Use the caches to create a new provider with caching
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 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 = StateProviderDatabase::new(state_provider);

View File

@@ -11,11 +11,9 @@ 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;
use rayon::prelude::*;
use reth_chain_state::{CanonicalInMemoryState, DeferredTrieData, ExecutedBlock};
use reth_consensus::{ConsensusError, FullConsensus};
use reth_engine_primitives::{
@@ -222,7 +220,7 @@ where
.map_err(NewPayloadError::other)?
.into();
let iter = Either::Left(iter.into_par_iter().map(Either::Left));
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)
@@ -232,9 +230,8 @@ where
Ok((iter, Box::new(convert) as Box<dyn Fn(_) -> _ + Send + Sync + 'static>))
}
BlockOrPayload::Block(block) => {
let iter = Either::Right(
block.body().clone_transactions().into_par_iter().map(Either::Right),
);
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)
@@ -371,7 +368,7 @@ where
)
.into())
};
let mut state_provider = ensure_ok!(provider_builder.build());
let state_provider = ensure_ok!(provider_builder.build());
drop(_enter);
// fetch parent block
@@ -402,14 +399,6 @@ where
// use prewarming background task
let txs = self.tx_iterator_for(&input)?;
// Extract the BAL, if valid and available
let block_access_list = ensure_ok!(input
.block_access_list()
.transpose()
// Eventually gets converted to a `InsertBlockErrorKind::Other`
.map_err(Box::<dyn std::error::Error + Send + Sync>::from))
.map(Arc::new);
// Spawn the appropriate processor based on strategy
let mut handle = ensure_ok!(self.spawn_payload_processor(
env.clone(),
@@ -418,24 +407,22 @@ where
parent_hash,
ctx.state(),
strategy,
block_access_list,
));
// Use cached state provider before executing, used in execution after prewarming threads
// complete
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,
));
};
let state_provider = CachedStateProvider::new_with_caches(
state_provider,
handle.caches(),
handle.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, "engine");
self.execute_block(&state_provider, env, &input, &mut handle)
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
} else {
self.execute_block(&state_provider, env, &input, &mut handle)
} {
@@ -788,7 +775,6 @@ where
parent_hash: B256,
state: &EngineApiTreeState<N>,
strategy: StateRootStrategy,
block_access_list: Option<Arc<BlockAccessList>>,
) -> Result<
PayloadHandle<
impl ExecutableTxFor<Evm> + use<N, P, Evm, V, T>,
@@ -818,14 +804,12 @@ where
.record(trie_input_start.elapsed().as_secs_f64());
let spawn_start = Instant::now();
let handle = self.payload_processor.spawn(
env,
txs,
provider_builder,
multiproof_provider_factory,
&self.config,
block_access_list,
);
// record prewarming initialization duration
@@ -888,7 +872,7 @@ where
/// Note: Use state root task only if prefix sets are empty, otherwise proof generation is
/// too expensive because it requires walking all paths in every proof.
const fn plan_state_root_computation(&self) -> StateRootStrategy {
if self.config.state_root_fallback() {
if self.config.state_root_fallback() || !self.config.has_enough_parallelism() {
StateRootStrategy::Synchronous
} else if self.config.use_state_root_task() {
StateRootStrategy::StateRootTask
@@ -1259,10 +1243,4 @@ 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

@@ -16,7 +16,7 @@ reth-primitives-traits.workspace = true
reth-errors.workspace = true
reth-chainspec.workspace = true
reth-fs-util.workspace = true
reth-engine-primitives = { workspace = true, features = ["std"] }
reth-engine-primitives.workspace = true
reth-engine-tree.workspace = true
reth-evm.workspace = true
reth-revm.workspace = true

View File

@@ -150,12 +150,6 @@ where
let era1_id = Era1Id::new(&config.network, start_block, block_count as u32)
.with_hash(historical_root);
let era1_id = if config.max_blocks_per_file == MAX_BLOCKS_PER_ERA1 as u64 {
era1_id
} else {
era1_id.with_era_count()
};
debug!("Final file name {}", era1_id.to_file_name());
let file_path = config.dir.join(era1_id.to_file_name());
let file = std::fs::File::create(&file_path)?;

View File

@@ -252,7 +252,7 @@ where
/// Extracts block headers and bodies from `iter` and appends them using `writer` and `provider`.
///
/// Collects hash to height using `hash_collector`.
/// Adds on to `total_difficulty` and 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

@@ -24,7 +24,7 @@ fn test_export_with_genesis_only() {
assert!(file_path.exists(), "Exported file should exist on disk");
let file_name = file_path.file_name().unwrap().to_str().unwrap();
assert!(
file_name.starts_with("mainnet-00000-"),
file_name.starts_with("mainnet-00000-00001-"),
"File should have correct prefix with era format"
);
assert!(file_name.ends_with(".era1"), "File should have correct extension");

View File

@@ -30,11 +30,8 @@ pub trait EraFileFormat: Sized {
/// Era file identifiers
pub trait EraFileId: Clone {
/// File type for this identifier
const FILE_TYPE: EraFileType;
/// Number of items, slots for `era`, blocks for `era1`, per era
const ITEMS_PER_ERA: u64;
/// Convert to standardized file name
fn to_file_name(&self) -> String;
/// Get the network name
fn network_name(&self) -> &str;
@@ -44,43 +41,6 @@ pub trait EraFileId: Clone {
/// Get the count of items
fn count(&self) -> u32;
/// Get the optional hash identifier
fn hash(&self) -> Option<[u8; 4]>;
/// Whether to include era count in filename
fn include_era_count(&self) -> bool;
/// Calculate era number
fn era_number(&self) -> u64 {
self.start_number() / Self::ITEMS_PER_ERA
}
/// Calculate the number of eras spanned per file.
///
/// If the user can decide how many slots/blocks per era file there are, we need to calculate
/// it. Most of the time it should be 1, but it can never be more than 2 eras per file
/// as there is a maximum of 8192 slots/blocks per era file.
fn era_count(&self) -> u64 {
if self.count() == 0 {
return 0;
}
let first_era = self.era_number();
let last_number = self.start_number() + self.count() as u64 - 1;
let last_era = last_number / Self::ITEMS_PER_ERA;
last_era - first_era + 1
}
/// Convert to standardized file name.
fn to_file_name(&self) -> String {
Self::FILE_TYPE.format_filename(
self.network_name(),
self.era_number(),
self.hash(),
self.include_era_count(),
self.era_count(),
)
}
}
/// [`StreamReader`] for reading era-format files
@@ -194,37 +154,6 @@ impl EraFileType {
}
}
/// Generate era file name.
///
/// Standard format: `<config-name>-<era-number>-<short-historical-root>.<ext>`
/// See also <https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era.md#file-name>
///
/// With era count (for custom exports):
/// `<config-name>-<era-number>-<era-count>-<short-historical-root>.<ext>`
pub fn format_filename(
&self,
network_name: &str,
era_number: u64,
hash: Option<[u8; 4]>,
include_era_count: bool,
era_count: u64,
) -> String {
let hash = format_hash(hash);
if include_era_count {
format!(
"{}-{:05}-{:05}-{}{}",
network_name,
era_number,
era_count,
hash,
self.extension()
)
} else {
format!("{}-{:05}-{}{}", network_name, era_number, hash, self.extension())
}
}
/// Detect file type from URL
/// By default, it assumes `Era` type
pub fn from_url(url: &str) -> Self {
@@ -235,11 +164,3 @@ impl EraFileType {
}
}
}
/// Format hash as hex string, or placeholder if none
pub fn format_hash(hash: Option<[u8; 4]>) -> String {
match hash {
Some(h) => format!("{:02x}{:02x}{:02x}{:02x}", h[0], h[1], h[2], h[3]),
None => "00000000".to_string(),
}
}

View File

@@ -3,7 +3,7 @@
//! See also <https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era.md>
use crate::{
common::file_ops::{EraFileId, EraFileType},
common::file_ops::EraFileId,
e2s::types::{Entry, IndexEntry, SLOT_INDEX},
era::types::consensus::{CompressedBeaconState, CompressedSignedBeaconBlock},
};
@@ -163,22 +163,12 @@ pub struct EraId {
/// Optional hash identifier for this file
/// First 4 bytes of the last historical root in the last state in the era file
pub hash: Option<[u8; 4]>,
/// Whether to include era count in filename
/// It is used for custom exports when we don't use the max number of items per file
include_era_count: bool,
}
impl EraId {
/// Create a new [`EraId`]
pub fn new(network_name: impl Into<String>, start_slot: u64, slot_count: u32) -> Self {
Self {
network_name: network_name.into(),
start_slot,
slot_count,
hash: None,
include_era_count: false,
}
Self { network_name: network_name.into(), start_slot, slot_count, hash: None }
}
/// Add a hash identifier to [`EraId`]
@@ -187,18 +177,32 @@ impl EraId {
self
}
/// Include era count in filename, for custom slot-per-file exports
pub const fn with_era_count(mut self) -> Self {
self.include_era_count = true;
self
/// Calculate which era number the file starts at
pub const fn era_number(&self) -> u64 {
self.start_slot / SLOTS_PER_HISTORICAL_ROOT
}
// Helper function to calculate the number of eras per era1 file,
// If the user can decide how many blocks per era1 file there are, we need to calculate it.
// Most of the time it should be 1, but it can never be more than 2 eras per file
// as there is a maximum of 8192 blocks per era1 file.
const fn calculate_era_count(&self) -> u64 {
if self.slot_count == 0 {
return 0;
}
let first_era = self.era_number();
// Calculate the actual last slot number in the range
let last_slot = self.start_slot + self.slot_count as u64 - 1;
// Find which era the last block belongs to
let last_era = last_slot / SLOTS_PER_HISTORICAL_ROOT;
// Count how many eras we span
last_era - first_era + 1
}
}
impl EraFileId for EraId {
const FILE_TYPE: EraFileType = EraFileType::Era;
const ITEMS_PER_ERA: u64 = SLOTS_PER_HISTORICAL_ROOT;
fn network_name(&self) -> &str {
&self.network_name
}
@@ -210,13 +214,24 @@ impl EraFileId for EraId {
fn count(&self) -> u32 {
self.slot_count
}
/// Convert to file name following the era file naming:
/// `<config-name>-<era-number>-<era-count>-<short-historical-root>.era`
/// <https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era.md#file-name>
/// See also <https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era.md>
fn to_file_name(&self) -> String {
let era_number = self.era_number();
let era_count = self.calculate_era_count();
fn hash(&self) -> Option<[u8; 4]> {
self.hash
}
fn include_era_count(&self) -> bool {
self.include_era_count
if let Some(hash) = self.hash {
format!(
"{}-{:05}-{:05}-{:02x}{:02x}{:02x}{:02x}.era",
self.network_name, era_number, era_count, hash[0], hash[1], hash[2], hash[3]
)
} else {
// era spec format with placeholder hash when no hash available
// Format: `<config-name>-<era-number>-<era-count>-00000000.era`
format!("{}-{:05}-{:05}-00000000.era", self.network_name, era_number, era_count)
}
}
}
@@ -384,40 +399,4 @@ mod tests {
let parsed_offset = index.offsets[0];
assert_eq!(parsed_offset, -1024);
}
#[test_case::test_case(
EraId::new("mainnet", 0, 8192).with_hash([0x4b, 0x36, 0x3d, 0xb9]),
"mainnet-00000-4b363db9.era";
"Mainnet era 0"
)]
#[test_case::test_case(
EraId::new("mainnet", 8192, 8192).with_hash([0x40, 0xcf, 0x2f, 0x3c]),
"mainnet-00001-40cf2f3c.era";
"Mainnet era 1"
)]
#[test_case::test_case(
EraId::new("mainnet", 0, 8192),
"mainnet-00000-00000000.era";
"Without hash"
)]
fn test_era_id_file_naming(id: EraId, expected_file_name: &str) {
let actual_file_name = id.to_file_name();
assert_eq!(actual_file_name, expected_file_name);
}
// File naming with era-count, for custom exports
#[test_case::test_case(
EraId::new("mainnet", 0, 8192).with_hash([0x4b, 0x36, 0x3d, 0xb9]).with_era_count(),
"mainnet-00000-00001-4b363db9.era";
"Mainnet era 0 with count"
)]
#[test_case::test_case(
EraId::new("mainnet", 8000, 500).with_hash([0xab, 0xcd, 0xef, 0x12]).with_era_count(),
"mainnet-00000-00002-abcdef12.era";
"Spanning two eras with count"
)]
fn test_era_id_file_naming_with_era_count(id: EraId, expected_file_name: &str) {
let actual_file_name = id.to_file_name();
assert_eq!(actual_file_name, expected_file_name);
}
}

View File

@@ -3,7 +3,7 @@
//! See also <https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era1.md>
use crate::{
common::file_ops::{EraFileId, EraFileType},
common::file_ops::EraFileId,
e2s::types::{Entry, IndexEntry},
era1::types::execution::{Accumulator, BlockTuple, MAX_BLOCKS_PER_ERA1},
};
@@ -105,10 +105,6 @@ pub struct Era1Id {
/// Optional hash identifier for this file
/// First 4 bytes of the last historical root in the last state in the era file
pub hash: Option<[u8; 4]>,
/// Whether to include era count in filename
/// It is used for custom exports when we don't use the max number of items per file
pub include_era_count: bool,
}
impl Era1Id {
@@ -118,13 +114,7 @@ impl Era1Id {
start_block: BlockNumber,
block_count: u32,
) -> Self {
Self {
network_name: network_name.into(),
start_block,
block_count,
hash: None,
include_era_count: false,
}
Self { network_name: network_name.into(), start_block, block_count, hash: None }
}
/// Add a hash identifier to [`Era1Id`]
@@ -133,17 +123,21 @@ impl Era1Id {
self
}
/// Include era count in filename, for custom block-per-file exports
pub const fn with_era_count(mut self) -> Self {
self.include_era_count = true;
self
// Helper function to calculate the number of eras per era1 file,
// If the user can decide how many blocks per era1 file there are, we need to calculate it.
// Most of the time it should be 1, but it can never be more than 2 eras per file
// as there is a maximum of 8192 blocks per era1 file.
const fn calculate_era_count(&self, first_era: u64) -> u64 {
// Calculate the actual last block number in the range
let last_block = self.start_block + self.block_count as u64 - 1;
// Find which era the last block belongs to
let last_era = last_block / MAX_BLOCKS_PER_ERA1 as u64;
// Count how many eras we span
last_era - first_era + 1
}
}
impl EraFileId for Era1Id {
const FILE_TYPE: EraFileType = EraFileType::Era1;
const ITEMS_PER_ERA: u64 = MAX_BLOCKS_PER_ERA1 as u64;
fn network_name(&self) -> &str {
&self.network_name
}
@@ -155,13 +149,24 @@ impl EraFileId for Era1Id {
fn count(&self) -> u32 {
self.block_count
}
fn hash(&self) -> Option<[u8; 4]> {
self.hash
}
fn include_era_count(&self) -> bool {
self.include_era_count
/// Convert to file name following the era file naming:
/// `<config-name>-<era-number>-<era-count>-<short-historical-root>.era(1)`
/// <https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era.md#file-name>
/// See also <https://github.com/eth-clients/e2store-format-specs/blob/main/formats/era1.md>
fn to_file_name(&self) -> String {
// Find which era the first block belongs to
let era_number = self.start_block / MAX_BLOCKS_PER_ERA1 as u64;
let era_count = self.calculate_era_count(era_number);
if let Some(hash) = self.hash {
format!(
"{}-{:05}-{:05}-{:02x}{:02x}{:02x}{:02x}.era1",
self.network_name, era_number, era_count, hash[0], hash[1], hash[2], hash[3]
)
} else {
// era spec format with placeholder hash when no hash available
// Format: `<config-name>-<era-number>-<era-count>-00000000.era1`
format!("{}-{:05}-{:05}-00000000.era1", self.network_name, era_number, era_count)
}
}
}
@@ -309,51 +314,35 @@ mod tests {
#[test_case::test_case(
Era1Id::new("mainnet", 0, 8192).with_hash([0x5e, 0xc1, 0xff, 0xb8]),
"mainnet-00000-5ec1ffb8.era1";
"mainnet-00000-00001-5ec1ffb8.era1";
"Mainnet era 0"
)]
#[test_case::test_case(
Era1Id::new("mainnet", 8192, 8192).with_hash([0x5e, 0xcb, 0x9b, 0xf9]),
"mainnet-00001-5ecb9bf9.era1";
"mainnet-00001-00001-5ecb9bf9.era1";
"Mainnet era 1"
)]
#[test_case::test_case(
Era1Id::new("sepolia", 0, 8192).with_hash([0x90, 0x91, 0x84, 0x72]),
"sepolia-00000-90918472.era1";
"sepolia-00000-00001-90918472.era1";
"Sepolia era 0"
)]
#[test_case::test_case(
Era1Id::new("sepolia", 155648, 8192).with_hash([0xfa, 0x77, 0x00, 0x19]),
"sepolia-00019-fa770019.era1";
"sepolia-00019-00001-fa770019.era1";
"Sepolia era 19"
)]
#[test_case::test_case(
Era1Id::new("mainnet", 1000, 100),
"mainnet-00000-00000000.era1";
"mainnet-00000-00001-00000000.era1";
"ID without hash"
)]
#[test_case::test_case(
Era1Id::new("sepolia", 101130240, 8192).with_hash([0xab, 0xcd, 0xef, 0x12]),
"sepolia-12345-abcdef12.era1";
"sepolia-12345-00001-abcdef12.era1";
"Large block number era 12345"
)]
fn test_era1_id_file_naming(id: Era1Id, expected_file_name: &str) {
let actual_file_name = id.to_file_name();
assert_eq!(actual_file_name, expected_file_name);
}
// File naming with era-count, for custom exports
#[test_case::test_case(
Era1Id::new("mainnet", 0, 8192).with_hash([0x5e, 0xc1, 0xff, 0xb8]).with_era_count(),
"mainnet-00000-00001-5ec1ffb8.era1";
"Mainnet era 0 with count"
)]
#[test_case::test_case(
Era1Id::new("mainnet", 8000, 500).with_hash([0xab, 0xcd, 0xef, 0x12]).with_era_count(),
"mainnet-00000-00002-abcdef12.era1";
"Spanning two eras with count"
)]
fn test_era1_id_file_naming_with_era_count(id: Era1Id, expected_file_name: &str) {
fn test_era1id_file_naming(id: Era1Id, expected_file_name: &str) {
let actual_file_name = id.to_file_name();
assert_eq!(actual_file_name, expected_file_name);
}

View File

@@ -154,9 +154,7 @@ where
Commands::ImportEra(command) => runner.run_blocking_until_ctrl_c(command.execute::<N>()),
Commands::ExportEra(command) => runner.run_blocking_until_ctrl_c(command.execute::<N>()),
Commands::DumpGenesis(command) => runner.run_blocking_until_ctrl_c(command.execute()),
Commands::Db(command) => {
runner.run_blocking_command_until_exit(|ctx| command.execute::<N>(ctx))
}
Commands::Db(command) => runner.run_blocking_until_ctrl_c(command.execute::<N>()),
Commands::Download(command) => runner.run_blocking_until_ctrl_c(command.execute::<N>()),
Commands::Stage(command) => {
runner.run_command_until_exit(|ctx| command.execute::<N, _>(ctx, components))

View File

@@ -5,6 +5,7 @@ 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;
@@ -16,12 +17,14 @@ 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 const fn new(chain_spec: Arc<ChainSpec>) -> Self {
Self { chain_spec }
pub fn new(chain_spec: Arc<ChainSpec>) -> Self {
Self { chain_spec, extra_data: Default::default() }
}
}
@@ -107,7 +110,7 @@ where
gas_limit: evm_env.block_env.gas_limit(),
difficulty: evm_env.block_env.difficulty(),
gas_used: *gas_used,
extra_data: ctx.extra_data,
extra_data: self.extra_data.clone(),
parent_beacon_block_root: ctx.parent_beacon_block_root,
blob_gas_used: block_blob_gas_used,
excess_blob_gas,

View File

@@ -19,36 +19,31 @@ extern crate alloc;
use alloc::{borrow::Cow, sync::Arc};
use alloy_consensus::Header;
use alloy_eips::Decodable2718;
pub use alloy_evm::EthEvm;
use alloy_evm::{
eth::{EthBlockExecutionCtx, EthBlockExecutorFactory},
EthEvmFactory, FromRecoveredTx, FromTxWithEncoded,
};
use alloy_primitives::{Bytes, U256};
use alloy_rpc_types_engine::ExecutionData;
use core::{convert::Infallible, fmt::Debug};
use reth_chainspec::{ChainSpec, EthChainSpec, MAINNET};
use reth_chainspec::{ChainSpec, EthChainSpec, EthereumHardforks, MAINNET};
use reth_ethereum_primitives::{Block, EthPrimitives, TransactionSigned};
use reth_evm::{
eth::NextEvmEnvAttributes, precompiles::PrecompilesMap, ConfigureEvm, EvmEnv, EvmFactory,
NextBlockEnvAttributes, TransactionEnv,
eth::NextEvmEnvAttributes, precompiles::PrecompilesMap, ConfigureEngineEvm, ConfigureEvm,
EvmEnv, EvmEnvFor, EvmFactory, ExecutableTxIterator, ExecutionCtxFor, NextBlockEnvAttributes,
TransactionEnv,
};
use reth_primitives_traits::{SealedBlock, SealedHeader};
use revm::{context::BlockEnv, primitives::hardfork::SpecId};
#[cfg(feature = "std")]
use reth_evm::{ConfigureEngineEvm, ExecutableTxIterator};
#[allow(unused_imports)]
use {
alloy_eips::Decodable2718,
alloy_primitives::{Bytes, U256},
alloy_rpc_types_engine::ExecutionData,
reth_chainspec::EthereumHardforks,
reth_evm::{EvmEnvFor, ExecutionCtxFor},
reth_primitives_traits::{constants::MAX_TX_GAS_LIMIT_OSAKA, SignedTransaction, TxTy},
reth_storage_errors::any::AnyError,
revm::context::CfgEnv,
revm::context_interface::block::BlobExcessGasAndPrice,
use reth_primitives_traits::{
constants::MAX_TX_GAS_LIMIT_OSAKA, SealedBlock, SealedHeader, SignedTransaction, TxTy,
};
use reth_storage_errors::any::AnyError;
use revm::{
context::{BlockEnv, CfgEnv},
context_interface::block::BlobExcessGasAndPrice,
primitives::hardfork::SpecId,
};
pub use alloy_evm::EthEvm;
mod config;
use alloy_evm::eth::spec::EthExecutorSpec;
@@ -121,6 +116,12 @@ 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>
@@ -192,7 +193,6 @@ 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,12 +206,10 @@ where
parent_beacon_block_root: attributes.parent_beacon_block_root,
ommers: &[],
withdrawals: attributes.withdrawals.map(Cow::Owned),
extra_data: attributes.extra_data,
})
}
}
#[cfg(feature = "std")]
impl<ChainSpec, EvmF> ConfigureEngineEvm<ExecutionData> for EthEvmConfig<ChainSpec, EvmF>
where
ChainSpec: EthExecutorSpec + EthChainSpec<Header = Header> + Hardforks + 'static,
@@ -284,7 +282,6 @@ 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(),
})
}
@@ -292,7 +289,7 @@ where
&self,
payload: &ExecutionData,
) -> Result<impl ExecutableTxIterator<Self>, Self::Error> {
let txs = payload.payload.transactions().clone();
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)?;

View File

@@ -24,7 +24,7 @@ reth-provider.workspace = true
reth-transaction-pool.workspace = true
reth-network.workspace = true
reth-evm.workspace = true
reth-evm-ethereum = { workspace = true, features = ["std"] }
reth-evm-ethereum.workspace = true
reth-rpc.workspace = true
reth-rpc-api.workspace = true
reth-rpc-eth-api.workspace = true
@@ -35,7 +35,7 @@ reth-chainspec.workspace = true
reth-revm = { workspace = true, features = ["std"] }
reth-rpc-eth-types.workspace = true
reth-engine-local.workspace = true
reth-engine-primitives = { workspace = true, features = ["std"] }
reth-engine-primitives.workspace = true
reth-payload-primitives.workspace = true
# ethereum
@@ -61,8 +61,6 @@ 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
@@ -88,9 +86,6 @@ asm-keccak = [
"reth-node-core/asm-keccak",
"revm/asm-keccak",
]
keccak-cache-global = [
"alloy-primitives/keccak-cache-global",
]
js-tracer = [
"reth-node-builder/js-tracer",
"reth-rpc/js-tracer",

View File

@@ -32,15 +32,15 @@ use reth_node_builder::{
EngineValidatorBuilder, EthApiBuilder, EthApiCtx, Identity, PayloadValidatorBuilder,
RethRpcAddOns, RpcAddOns, RpcHandle,
},
BuilderContext, DebugNode, Node, NodeAdapter,
BuilderContext, DebugNode, Node, NodeAdapter, PayloadBuilderConfig,
};
use reth_payload_primitives::PayloadTypes;
use reth_provider::{providers::ProviderFactoryBuilder, EthStorage};
use reth_rpc::{
eth::core::{EthApiFor, EthRpcConverterFor},
TestingApi, ValidationApi,
ValidationApi,
};
use reth_rpc_api::servers::{BlockSubmissionValidationApiServer, TestingApiServer};
use reth_rpc_api::servers::BlockSubmissionValidationApiServer;
use reth_rpc_builder::{config::RethRpcServerConfig, middleware::RethRpcMiddleware};
use reth_rpc_eth_api::{
helpers::{
@@ -118,14 +118,13 @@ impl EthereumNode {
/// use reth_chainspec::ChainSpecBuilder;
/// use reth_db::open_db_read_only;
/// use reth_node_ethereum::EthereumNode;
/// use reth_provider::providers::{RocksDBProvider, StaticFileProvider};
/// use reth_provider::providers::StaticFileProvider;
/// use std::sync::Arc;
///
/// let factory = EthereumNode::provider_factory_builder()
/// .db(Arc::new(open_db_read_only("db", Default::default()).unwrap()))
/// .chainspec(ChainSpecBuilder::mainnet().build().into())
/// .static_file(StaticFileProvider::read_only("db/static_files", false).unwrap())
/// .rocksdb_provider(RocksDBProvider::builder("db/rocksdb").build().unwrap())
/// .build_provider_factory();
/// ```
pub fn provider_factory_builder() -> ProviderFactoryBuilder<Self> {
@@ -314,17 +313,6 @@ 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
@@ -438,7 +426,9 @@ where
type EVM = EthEvmConfig<Types::ChainSpec>;
async fn build_evm(self, ctx: &BuilderContext<Node>) -> eyre::Result<Self::EVM> {
Ok(EthEvmConfig::new(ctx.chain_spec()))
let evm_config = EthEvmConfig::new(ctx.chain_spec())
.with_extra_data(ctx.payload_builder_config().extra_data_bytes());
Ok(evm_config)
}
}

View File

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

View File

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

View File

@@ -1,85 +0,0 @@
//! 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")),
rocksdb_path: Some(tempdir.path().join("rocksdb")),
};
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

@@ -24,7 +24,7 @@ reth-payload-builder-primitives.workspace = true
reth-payload-primitives.workspace = true
reth-basic-payload-builder.workspace = true
reth-evm.workspace = true
reth-evm-ethereum = { workspace = true, features = ["std"] }
reth-evm-ethereum.workspace = true
reth-errors.workspace = true
reth-chainspec.workspace = true
reth-payload-validator.workspace = true

View File

@@ -1,5 +1,4 @@
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.
@@ -14,8 +13,6 @@ 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 {
@@ -31,7 +28,6 @@ impl EthereumBuilderConfig {
desired_gas_limit: ETHEREUM_BLOCK_GAS_LIMIT_30M,
await_payload_on_missing: true,
max_blobs_per_block: None,
extra_data: Bytes::new(),
}
}
@@ -53,12 +49,6 @@ 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,7 +168,6 @@ 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

@@ -79,9 +79,7 @@ arbitrary = [
"alloy-rpc-types-engine?/arbitrary",
"reth-codecs?/arbitrary",
]
keccak-cache-global = [
"reth-node-ethereum?/keccak-cache-global",
]
test-utils = [
"reth-chainspec/test-utils",
"reth-consensus?/test-utils",

View File

@@ -32,7 +32,6 @@ auto_impl.workspace = true
derive_more.workspace = true
futures-util.workspace = true
metrics = { workspace = true, optional = true }
rayon = { workspace = true, optional = true }
[dev-dependencies]
reth-ethereum-primitives.workspace = true
@@ -41,7 +40,6 @@ reth-ethereum-forks.workspace = true
[features]
default = ["std"]
std = [
"dep:rayon",
"reth-primitives-traits/std",
"alloy-eips/std",
"alloy-primitives/std",

View File

@@ -1,5 +1,4 @@
use crate::{execute::ExecutableTxFor, ConfigureEvm, EvmEnvFor, ExecutionCtxFor};
use rayon::prelude::*;
/// [`ConfigureEvm`] extension providing methods for executing payloads.
pub trait ConfigureEngineEvm<ExecutionData>: ConfigureEvm {
@@ -22,7 +21,7 @@ pub trait ConfigureEngineEvm<ExecutionData>: ConfigureEvm {
/// 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::IntoIter, Self::Convert)> + Send + 'static {
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,
@@ -33,10 +32,8 @@ pub trait ExecutableTxTuple: Into<(Self::IntoIter, Self::Convert)> + Send + 'sta
/// Errors that may occur while recovering or decoding transactions.
type Error: core::error::Error + Send + Sync + 'static;
/// Iterator over [`ExecutableTxTuple::Tx`].
type IntoIter: IntoParallelIterator<Item = Self::RawTx, Iter: IndexedParallelIterator>
+ Send
+ '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.
@@ -48,14 +45,14 @@ where
RawTx: Send + Sync + 'static,
Tx: Clone + Send + Sync + 'static,
Err: core::error::Error + Send + Sync + 'static,
I: IntoParallelIterator<Item = RawTx, Iter: IndexedParallelIterator> + 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 IntoIter = I;
type Iter = I;
type Convert = F;
}

View File

@@ -28,7 +28,7 @@ use alloy_evm::{
block::{BlockExecutorFactory, BlockExecutorFor},
precompiles::PrecompilesMap,
};
use alloy_primitives::{Address, Bytes, B256};
use alloy_primitives::{Address, B256};
use core::{error::Error, fmt::Debug};
use execute::{BasicBlockExecutor, BlockAssembler, BlockBuilder};
use reth_execution_errors::BlockExecutionError;
@@ -44,10 +44,8 @@ pub mod execute;
mod aliases;
pub use aliases::*;
#[cfg(feature = "std")]
mod engine;
#[cfg(feature = "std")]
pub use engine::{ConfigureEngineEvm, ExecutableTxIterator, ExecutableTxTuple};
pub use engine::{ConfigureEngineEvm, ExecutableTxIterator};
#[cfg(feature = "metrics")]
pub mod metrics;
@@ -503,8 +501,6 @@ 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,14 +17,6 @@ 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

@@ -20,9 +20,7 @@ use futures_util::FutureExt;
use reth_chainspec::{ChainSpec, MAINNET};
use reth_consensus::test_utils::TestConsensus;
use reth_db::{
test_utils::{
create_test_rocksdb_dir, create_test_rw_db, create_test_static_files_dir, TempDatabase,
},
test_utils::{create_test_rw_db, create_test_static_files_dir, TempDatabase},
DatabaseEnv,
};
use reth_db_common::init::init_genesis;
@@ -52,7 +50,7 @@ use reth_node_ethereum::{
use reth_payload_builder::noop::NoopPayloadBuilderService;
use reth_primitives_traits::{Block as _, RecoveredBlock};
use reth_provider::{
providers::{BlockchainProvider, RocksDBProvider, StaticFileProvider},
providers::{BlockchainProvider, StaticFileProvider},
BlockReader, EthStorage, ProviderFactory,
};
use reth_tasks::TaskManager;
@@ -241,13 +239,11 @@ pub async fn test_exex_context_with_chain_spec(
let consensus = Arc::new(TestConsensus::default());
let (static_dir, _) = create_test_static_files_dir();
let (rocksdb_dir, _) = create_test_rocksdb_dir();
let db = create_test_rw_db();
let provider_factory = ProviderFactory::<NodeTypesWithDBAdapter<TestNode, _>>::new(
db,
chain_spec.clone(),
StaticFileProvider::read_write(static_dir.keep()).expect("static file provider"),
RocksDBProvider::builder(rocksdb_dir.keep()).build().unwrap(),
)?;
let genesis_hash = init_genesis(&provider_factory)?;

View File

@@ -95,33 +95,17 @@ 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,
{
/// 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>,
},
ChainCommitted { new: Chain<'a, N> },
ChainReorged { old: Chain<'a, N>, new: Chain<'a, N> },
ChainReverted { old: Chain<'a, N> },
}
impl<'a, N> From<&'a super::ExExNotification<N>> for ExExNotification<'a, N>

View File

@@ -24,7 +24,7 @@ pub struct Discv4Config {
/// The number of allowed consecutive failures for `FindNode` requests. Default: 5.
pub max_find_node_failures: u8,
/// The interval to use when checking for expired nodes that need to be re-pinged. Default:
/// 10 seconds.
/// 10min.
pub ping_interval: Duration,
/// The duration of we consider a ping timed out.
pub ping_expiration: Duration,
@@ -93,7 +93,7 @@ impl Discv4Config {
/// Returns the corresponding [`ResolveNatInterval`], if a [`NatResolver`] and an interval was
/// configured
pub fn resolve_external_ip_interval(&self) -> Option<ResolveNatInterval> {
let resolver = self.external_ip_resolver.clone()?;
let resolver = self.external_ip_resolver?;
let interval = self.resolve_external_ip_interval?;
Some(ResolveNatInterval::interval_at(resolver, tokio::time::Instant::now(), interval))
}
@@ -275,7 +275,10 @@ impl Discv4ConfigBuilder {
}
/// Configures if and how the external IP of the node should be resolved.
pub fn external_ip_resolver(&mut self, external_ip_resolver: Option<NatResolver>) -> &mut Self {
pub const fn external_ip_resolver(
&mut self,
external_ip_resolver: Option<NatResolver>,
) -> &mut Self {
self.config.external_ip_resolver = external_ip_resolver;
self
}

View File

@@ -625,13 +625,10 @@ impl Discv4Service {
self.lookup_interval = tokio::time::interval(duration);
}
/// Sets the external Ip to the configured external IP if [`NatResolver::ExternalIp`] or
/// [`NatResolver::ExternalAddr`]. In the case of [`NatResolver::ExternalAddr`], it will return
/// the first IP address found for the domain associated with the discv4 UDP port.
/// Sets the external Ip to the configured external IP if [`NatResolver::ExternalIp`].
fn resolve_external_ip(&mut self) {
if let Some(r) = &self.resolve_external_ip_interval &&
let Some(external_ip) =
r.resolver().clone().as_external_ip(self.local_node_record.udp_port)
let Some(external_ip) = r.resolver().as_external_ip()
{
self.set_external_ip_addr(external_ip);
}

View File

@@ -1218,9 +1218,7 @@ impl ReverseHeadersDownloaderBuilder {
next_request_block_number: 0,
next_chain_tip_block_number: 0,
lowest_validated_header: None,
// TODO(mattsse): tmp hotfix to prevent issues with syncing from besu which has an upper
// limit of 512
request_limit: request_limit.min(512),
request_limit,
min_concurrent_requests,
max_concurrent_requests,
stream_batch_size,

View File

@@ -169,7 +169,7 @@ impl NewPooledTransactionHashes {
matches!(version, EthVersion::Eth67 | EthVersion::Eth66)
}
Self::Eth68(_) => {
matches!(version, EthVersion::Eth68 | EthVersion::Eth69 | EthVersion::Eth70)
matches!(version, EthVersion::Eth68 | EthVersion::Eth69)
}
}
}

View File

@@ -100,16 +100,6 @@ impl Capability {
Self::eth(EthVersion::Eth68)
}
/// Returns the [`EthVersion::Eth69`] capability.
pub const fn eth_69() -> Self {
Self::eth(EthVersion::Eth69)
}
/// Returns the [`EthVersion::Eth70`] capability.
pub const fn eth_70() -> Self {
Self::eth(EthVersion::Eth70)
}
/// Whether this is eth v66 protocol.
#[inline]
pub fn is_eth_v66(&self) -> bool {
@@ -128,26 +118,10 @@ impl Capability {
self.name == "eth" && self.version == 68
}
/// Whether this is eth v69.
#[inline]
pub fn is_eth_v69(&self) -> bool {
self.name == "eth" && self.version == 69
}
/// Whether this is eth v70.
#[inline]
pub fn is_eth_v70(&self) -> bool {
self.name == "eth" && self.version == 70
}
/// Whether this is any eth version.
#[inline]
pub fn is_eth(&self) -> bool {
self.is_eth_v66() ||
self.is_eth_v67() ||
self.is_eth_v68() ||
self.is_eth_v69() ||
self.is_eth_v70()
self.is_eth_v66() || self.is_eth_v67() || self.is_eth_v68()
}
}
@@ -167,7 +141,7 @@ impl From<EthVersion> for Capability {
#[cfg(any(test, feature = "arbitrary"))]
impl<'a> arbitrary::Arbitrary<'a> for Capability {
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let version = u.int_in_range(66..=70)?; // Valid eth protocol versions are 66-70
let version = u.int_in_range(66..=69)?; // Valid eth protocol versions are 66-69
// Only generate valid eth protocol name for now since it's the only supported protocol
Ok(Self::new_static("eth", version))
}
@@ -181,8 +155,6 @@ pub struct Capabilities {
eth_66: bool,
eth_67: bool,
eth_68: bool,
eth_69: bool,
eth_70: bool,
}
impl Capabilities {
@@ -192,8 +164,6 @@ impl Capabilities {
eth_66: value.iter().any(Capability::is_eth_v66),
eth_67: value.iter().any(Capability::is_eth_v67),
eth_68: value.iter().any(Capability::is_eth_v68),
eth_69: value.iter().any(Capability::is_eth_v69),
eth_70: value.iter().any(Capability::is_eth_v70),
inner: value,
}
}
@@ -212,7 +182,7 @@ impl Capabilities {
/// Whether the peer supports `eth` sub-protocol.
#[inline]
pub const fn supports_eth(&self) -> bool {
self.eth_70 || self.eth_69 || self.eth_68 || self.eth_67 || self.eth_66
self.eth_68 || self.eth_67 || self.eth_66
}
/// Whether this peer supports eth v66 protocol.
@@ -232,18 +202,6 @@ impl Capabilities {
pub const fn supports_eth_v68(&self) -> bool {
self.eth_68
}
/// Whether this peer supports eth v69 protocol.
#[inline]
pub const fn supports_eth_v69(&self) -> bool {
self.eth_69
}
/// Whether this peer supports eth v70 protocol.
#[inline]
pub const fn supports_eth_v70(&self) -> bool {
self.eth_70
}
}
impl From<Vec<Capability>> for Capabilities {
@@ -266,8 +224,6 @@ impl Decodable for Capabilities {
eth_66: inner.iter().any(Capability::is_eth_v66),
eth_67: inner.iter().any(Capability::is_eth_v67),
eth_68: inner.iter().any(Capability::is_eth_v68),
eth_69: inner.iter().any(Capability::is_eth_v69),
eth_70: inner.iter().any(Capability::is_eth_v70),
inner,
})
}

View File

@@ -1,4 +1,4 @@
//! Implements Ethereum wire protocol for versions 66 through 70.
//! Implements Ethereum wire protocol for versions 66, 67, and 68.
//! Defines structs/enums for messages, request-response pairs, and broadcasts.
//! Handles compatibility with [`EthVersion`].
//!
@@ -8,13 +8,13 @@
use super::{
broadcast::NewBlockHashes, BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders,
GetNodeData, GetPooledTransactions, GetReceipts, GetReceipts70, NewPooledTransactionHashes66,
GetNodeData, GetPooledTransactions, GetReceipts, NewPooledTransactionHashes66,
NewPooledTransactionHashes68, NodeData, PooledTransactions, Receipts, Status, StatusEth69,
Transactions,
};
use crate::{
status::StatusMessage, BlockRangeUpdate, EthNetworkPrimitives, EthVersion, NetworkPrimitives,
RawCapabilityMessage, Receipts69, Receipts70, SharedTransactions,
RawCapabilityMessage, Receipts69, SharedTransactions,
};
use alloc::{boxed::Box, string::String, sync::Arc};
use alloy_primitives::{
@@ -111,29 +111,13 @@ impl<N: NetworkPrimitives> ProtocolMessage<N> {
}
EthMessage::NodeData(RequestPair::decode(buf)?)
}
EthMessageID::GetReceipts => {
if version >= EthVersion::Eth70 {
EthMessage::GetReceipts70(RequestPair::decode(buf)?)
} else {
EthMessage::GetReceipts(RequestPair::decode(buf)?)
}
}
EthMessageID::GetReceipts => EthMessage::GetReceipts(RequestPair::decode(buf)?),
EthMessageID::Receipts => {
match version {
v if v >= EthVersion::Eth70 => {
// eth/70 continues to omit bloom filters and adds the
// `lastBlockIncomplete` flag, encoded as
// `[request-id, lastBlockIncomplete, [[receipt₁, receipt₂], ...]]`.
EthMessage::Receipts70(RequestPair::decode(buf)?)
}
EthVersion::Eth69 => {
// with eth69, receipts no longer include the bloom
EthMessage::Receipts69(RequestPair::decode(buf)?)
}
_ => {
// before eth69 we need to decode the bloom as well
EthMessage::Receipts(RequestPair::decode(buf)?)
}
if version < EthVersion::Eth69 {
EthMessage::Receipts(RequestPair::decode(buf)?)
} else {
// with eth69, receipts no longer include the bloom
EthMessage::Receipts69(RequestPair::decode(buf)?)
}
}
EthMessageID::BlockRangeUpdate => {
@@ -221,9 +205,6 @@ impl<N: NetworkPrimitives> From<EthBroadcastMessage<N>> for ProtocolBroadcastMes
///
/// The `eth/69` announces the historical block range served by the node. Removes total difficulty
/// information. And removes the Bloom field from receipts transferred over the protocol.
///
/// The `eth/70` (EIP-7975) keeps the eth/69 status format and introduces partial receipts.
/// requests/responses.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum EthMessage<N: NetworkPrimitives = EthNetworkPrimitives> {
@@ -278,12 +259,6 @@ pub enum EthMessage<N: NetworkPrimitives = EthNetworkPrimitives> {
NodeData(RequestPair<NodeData>),
/// Represents a `GetReceipts` request-response pair.
GetReceipts(RequestPair<GetReceipts>),
/// Represents a `GetReceipts` request for eth/70.
///
/// Note: Unlike earlier protocol versions, the eth/70 encoding for
/// `GetReceipts` in EIP-7975 inlines the request id. The type still wraps
/// a [`RequestPair`], but with a custom inline encoding.
GetReceipts70(RequestPair<GetReceipts70>),
/// Represents a Receipts request-response pair.
#[cfg_attr(
feature = "serde",
@@ -296,16 +271,6 @@ pub enum EthMessage<N: NetworkPrimitives = EthNetworkPrimitives> {
serde(bound = "N::Receipt: serde::Serialize + serde::de::DeserializeOwned")
)]
Receipts69(RequestPair<Receipts69<N::Receipt>>),
/// Represents a Receipts request-response pair for eth/70.
#[cfg_attr(
feature = "serde",
serde(bound = "N::Receipt: serde::Serialize + serde::de::DeserializeOwned")
)]
///
/// Note: The eth/70 encoding for `Receipts` in EIP-7975 inlines the
/// request id. The type still wraps a [`RequestPair`], but with a custom
/// inline encoding.
Receipts70(RequestPair<Receipts70<N::Receipt>>),
/// Represents a `BlockRangeUpdate` message broadcast to the network.
#[cfg_attr(
feature = "serde",
@@ -335,8 +300,8 @@ impl<N: NetworkPrimitives> EthMessage<N> {
Self::PooledTransactions(_) => EthMessageID::PooledTransactions,
Self::GetNodeData(_) => EthMessageID::GetNodeData,
Self::NodeData(_) => EthMessageID::NodeData,
Self::GetReceipts(_) | Self::GetReceipts70(_) => EthMessageID::GetReceipts,
Self::Receipts(_) | Self::Receipts69(_) | Self::Receipts70(_) => EthMessageID::Receipts,
Self::GetReceipts(_) => EthMessageID::GetReceipts,
Self::Receipts(_) | Self::Receipts69(_) => EthMessageID::Receipts,
Self::BlockRangeUpdate(_) => EthMessageID::BlockRangeUpdate,
Self::Other(msg) => EthMessageID::Other(msg.id as u8),
}
@@ -349,7 +314,6 @@ impl<N: NetworkPrimitives> EthMessage<N> {
Self::GetBlockBodies(_) |
Self::GetBlockHeaders(_) |
Self::GetReceipts(_) |
Self::GetReceipts70(_) |
Self::GetPooledTransactions(_) |
Self::GetNodeData(_)
)
@@ -362,40 +326,11 @@ impl<N: NetworkPrimitives> EthMessage<N> {
Self::PooledTransactions(_) |
Self::Receipts(_) |
Self::Receipts69(_) |
Self::Receipts70(_) |
Self::BlockHeaders(_) |
Self::BlockBodies(_) |
Self::NodeData(_)
)
}
/// Converts the message types where applicable.
///
/// This handles up/downcasting where appropriate, for example for different receipt request
/// types.
pub fn map_versioned(self, version: EthVersion) -> Self {
// For eth/70 peers we send `GetReceipts` using the new eth/70
// encoding with `firstBlockReceiptIndex = 0`, while keeping the
// user-facing `PeerRequest` API unchanged.
if version >= EthVersion::Eth70 {
return match self {
Self::GetReceipts(pair) => {
let RequestPair { request_id, message } = pair;
let req = RequestPair {
request_id,
message: GetReceipts70 {
first_block_receipt_index: 0,
block_hashes: message.0,
},
};
Self::GetReceipts70(req)
}
other => other,
}
}
self
}
}
impl<N: NetworkPrimitives> Encodable for EthMessage<N> {
@@ -416,10 +351,8 @@ impl<N: NetworkPrimitives> Encodable for EthMessage<N> {
Self::GetNodeData(request) => request.encode(out),
Self::NodeData(data) => data.encode(out),
Self::GetReceipts(request) => request.encode(out),
Self::GetReceipts70(request) => request.encode(out),
Self::Receipts(receipts) => receipts.encode(out),
Self::Receipts69(receipt69) => receipt69.encode(out),
Self::Receipts70(receipt70) => receipt70.encode(out),
Self::BlockRangeUpdate(block_range_update) => block_range_update.encode(out),
Self::Other(unknown) => out.put_slice(&unknown.payload),
}
@@ -441,10 +374,8 @@ impl<N: NetworkPrimitives> Encodable for EthMessage<N> {
Self::GetNodeData(request) => request.length(),
Self::NodeData(data) => data.length(),
Self::GetReceipts(request) => request.length(),
Self::GetReceipts70(request) => request.length(),
Self::Receipts(receipts) => receipts.length(),
Self::Receipts69(receipt69) => receipt69.length(),
Self::Receipts70(receipt70) => receipt70.length(),
Self::BlockRangeUpdate(block_range_update) => block_range_update.length(),
Self::Other(unknown) => unknown.length(),
}

View File

@@ -17,42 +17,6 @@ pub struct GetReceipts(
pub Vec<B256>,
);
/// Eth/70 `GetReceipts` request payload that supports partial receipt queries.
///
/// When used with eth/70, the request id is carried by the surrounding
/// [`crate::message::RequestPair`], and the on-wire shape is the flattened list
/// `firstBlockReceiptIndex, [blockhash₁, ...]`.
///
/// See also [eip-7975](https://eips.ethereum.org/EIPS/eip-7975)
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
pub struct GetReceipts70 {
/// Index into the receipts of the first requested block hash.
pub first_block_receipt_index: u64,
/// The block hashes to request receipts for.
pub block_hashes: Vec<B256>,
}
impl alloy_rlp::Encodable for GetReceipts70 {
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
self.first_block_receipt_index.encode(out);
self.block_hashes.encode(out);
}
fn length(&self) -> usize {
self.first_block_receipt_index.length() + self.block_hashes.length()
}
}
impl alloy_rlp::Decodable for GetReceipts70 {
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
let first_block_receipt_index = u64::decode(buf)?;
let block_hashes = Vec::<B256>::decode(buf)?;
Ok(Self { first_block_receipt_index, block_hashes })
}
}
/// The response to [`GetReceipts`], containing receipt lists that correspond to each block
/// requested.
#[derive(Clone, Debug, PartialEq, Eq, Default)]
@@ -94,13 +58,7 @@ pub struct Receipts69<T = Receipt>(pub Vec<Vec<T>>);
impl<T: TxReceipt> Receipts69<T> {
/// Encodes all receipts with the bloom filter.
///
/// Eth/69 omits bloom filters on the wire, while some internal callers
/// (and legacy APIs) still operate on [`Receipts`] with
/// [`ReceiptWithBloom`]. This helper reconstructs the bloom locally from
/// each receipt's logs so the older API can be used on top of eth/69 data.
///
/// Note: This is an expensive operation that recalculates the bloom for
/// every receipt.
/// Note: This is an expensive operation that recalculates the bloom for each receipt.
pub fn into_with_bloom(self) -> Receipts<T> {
Receipts(
self.0
@@ -117,68 +75,6 @@ impl<T: TxReceipt> From<Receipts69<T>> for Receipts<T> {
}
}
/// Eth/70 `Receipts` response payload.
///
/// This is used in conjunction with [`crate::message::RequestPair`] to encode the full wire
/// message `[request-id, lastBlockIncomplete, [[receipt₁, receipt₂], ...]]`.
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(any(test, feature = "arbitrary"), derive(arbitrary::Arbitrary))]
pub struct Receipts70<T = Receipt> {
/// Whether the receipts list for the last block is incomplete.
pub last_block_incomplete: bool,
/// Receipts grouped by block.
pub receipts: Vec<Vec<T>>,
}
impl<T> alloy_rlp::Encodable for Receipts70<T>
where
T: alloy_rlp::Encodable,
{
fn encode(&self, out: &mut dyn alloy_rlp::BufMut) {
self.last_block_incomplete.encode(out);
self.receipts.encode(out);
}
fn length(&self) -> usize {
self.last_block_incomplete.length() + self.receipts.length()
}
}
impl<T> alloy_rlp::Decodable for Receipts70<T>
where
T: alloy_rlp::Decodable,
{
fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
let last_block_incomplete = bool::decode(buf)?;
let receipts = Vec::<Vec<T>>::decode(buf)?;
Ok(Self { last_block_incomplete, receipts })
}
}
impl<T: TxReceipt> Receipts70<T> {
/// Encodes all receipts with the bloom filter.
///
/// Just like eth/69, eth/70 does not transmit bloom filters over the wire.
/// When higher layers still expect the older bloom-bearing [`Receipts`]
/// type, this helper converts the eth/70 payload into that shape by
/// recomputing the bloom locally from the contained receipts.
///
/// Note: This is an expensive operation that recalculates the bloom for
/// every receipt.
pub fn into_with_bloom(self) -> Receipts<T> {
// Reuse the eth/69 helper, since both variants carry the same
// receipt list shape (only eth/70 adds request metadata).
Receipts69(self.receipts).into_with_bloom()
}
}
impl<T: TxReceipt> From<Receipts70<T>> for Receipts<T> {
fn from(receipts: Receipts70<T>) -> Self {
receipts.into_with_bloom()
}
}
#[cfg(test)]
mod tests {
use super::*;
@@ -329,70 +225,4 @@ mod tests {
let encoded = alloy_rlp::encode(&request);
assert_eq!(encoded, data);
}
#[test]
fn encode_get_receipts70_inline_shape() {
let req = RequestPair {
request_id: 1111,
message: GetReceipts70 {
first_block_receipt_index: 0,
block_hashes: vec![
hex!("00000000000000000000000000000000000000000000000000000000deadc0de").into(),
hex!("00000000000000000000000000000000000000000000000000000000feedbeef").into(),
],
},
};
let mut out = vec![];
req.encode(&mut out);
let mut buf = out.as_slice();
let header = alloy_rlp::Header::decode(&mut buf).unwrap();
let payload_start = buf.len();
let request_id = u64::decode(&mut buf).unwrap();
let first_block_receipt_index = u64::decode(&mut buf).unwrap();
let block_hashes = Vec::<B256>::decode(&mut buf).unwrap();
assert!(buf.is_empty(), "buffer not fully consumed");
assert_eq!(request_id, 1111);
assert_eq!(first_block_receipt_index, 0);
assert_eq!(block_hashes.len(), 2);
// ensure payload length matches header
assert_eq!(payload_start - buf.len(), header.payload_length);
let mut buf = out.as_slice();
let decoded = RequestPair::<GetReceipts70>::decode(&mut buf).unwrap();
assert!(buf.is_empty(), "buffer not fully consumed on decode");
assert_eq!(decoded, req);
}
#[test]
fn encode_receipts70_inline_shape() {
let payload: Receipts70<Receipt> =
Receipts70 { last_block_incomplete: true, receipts: vec![vec![Receipt::default()]] };
let resp = RequestPair { request_id: 7, message: payload };
let mut out = vec![];
resp.encode(&mut out);
let mut buf = out.as_slice();
let header = alloy_rlp::Header::decode(&mut buf).unwrap();
let payload_start = buf.len();
let request_id = u64::decode(&mut buf).unwrap();
let last_block_incomplete = bool::decode(&mut buf).unwrap();
let receipts = Vec::<Vec<Receipt>>::decode(&mut buf).unwrap();
assert!(buf.is_empty(), "buffer not fully consumed");
assert_eq!(payload_start - buf.len(), header.payload_length);
assert_eq!(request_id, 7);
assert!(last_block_incomplete);
assert_eq!(receipts.len(), 1);
assert_eq!(receipts[0].len(), 1);
let mut buf = out.as_slice();
let decoded = RequestPair::<Receipts70>::decode(&mut buf).unwrap();
assert!(buf.is_empty(), "buffer not fully consumed on decode");
assert_eq!(decoded, resp);
}
}

View File

@@ -13,7 +13,7 @@ use reth_codecs_derive::add_arbitrary_tests;
/// unsupported fields are stripped out.
#[derive(Clone, Debug, PartialEq, Eq, Copy)]
pub struct UnifiedStatus {
/// The eth protocol version (e.g. eth/66 to eth/70).
/// The eth protocol version (e.g. eth/66 to eth/69).
pub version: EthVersion,
/// The chain ID identifying the peers network.
pub chain: Chain,
@@ -157,7 +157,7 @@ impl StatusBuilder {
self.status
}
/// Sets the eth protocol version (e.g., eth/66, eth/70).
/// Sets the eth protocol version (e.g., eth/66, eth/69).
pub const fn version(mut self, version: EthVersion) -> Self {
self.status.version = version;
self
@@ -378,8 +378,8 @@ impl Debug for StatusEth69 {
}
}
/// `StatusMessage` can store either the Legacy version (with TD), or the eth/69+/eth/70 version
/// (omits TD, includes block range).
/// `StatusMessage` can store either the Legacy version (with TD) or the
/// eth/69 version (omits TD).
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum StatusMessage {
@@ -546,24 +546,6 @@ mod tests {
assert_eq!(unified_status, roundtripped_unified_status);
}
#[test]
fn roundtrip_eth70() {
let unified_status = UnifiedStatus::builder()
.version(EthVersion::Eth70)
.chain(Chain::mainnet())
.genesis(MAINNET_GENESIS_HASH)
.forkid(ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 0 })
.blockhash(b256!("0xfeb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d"))
.total_difficulty(None)
.earliest_block(Some(1))
.latest_block(Some(2))
.build();
let status_message = unified_status.into_message();
let roundtripped_unified_status = UnifiedStatus::from_message(status_message);
assert_eq!(unified_status, roundtripped_unified_status);
}
#[test]
fn encode_eth69_status_message() {
let expected = hex!("f8544501a0d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3c684b715077d8083ed14f2840112a880a0feb27336ca7923f8fab3bd617fcb6e75841538f71c1bcfc267d7838489d9e13d");

View File

@@ -27,8 +27,6 @@ pub enum EthVersion {
Eth68 = 68,
/// The `eth` protocol version 69.
Eth69 = 69,
/// The `eth` protocol version 70.
Eth70 = 70,
}
impl EthVersion {
@@ -57,11 +55,6 @@ impl EthVersion {
pub const fn is_eth69(&self) -> bool {
matches!(self, Self::Eth69)
}
/// Returns true if the version is eth/70
pub const fn is_eth70(&self) -> bool {
matches!(self, Self::Eth70)
}
}
/// RLP encodes `EthVersion` as a single byte (66-69).
@@ -103,7 +96,6 @@ impl TryFrom<&str> for EthVersion {
"67" => Ok(Self::Eth67),
"68" => Ok(Self::Eth68),
"69" => Ok(Self::Eth69),
"70" => Ok(Self::Eth70),
_ => Err(ParseVersionError(s.to_string())),
}
}
@@ -128,7 +120,6 @@ impl TryFrom<u8> for EthVersion {
67 => Ok(Self::Eth67),
68 => Ok(Self::Eth68),
69 => Ok(Self::Eth69),
70 => Ok(Self::Eth70),
_ => Err(ParseVersionError(u.to_string())),
}
}
@@ -158,7 +149,6 @@ impl From<EthVersion> for &'static str {
EthVersion::Eth67 => "67",
EthVersion::Eth68 => "68",
EthVersion::Eth69 => "69",
EthVersion::Eth70 => "70",
}
}
}
@@ -205,7 +195,7 @@ impl Decodable for ProtocolVersion {
#[cfg(test)]
mod tests {
use super::EthVersion;
use super::{EthVersion, ParseVersionError};
use alloy_rlp::{Decodable, Encodable, Error as RlpError};
use bytes::BytesMut;
@@ -215,7 +205,7 @@ mod tests {
assert_eq!(EthVersion::Eth67, EthVersion::try_from("67").unwrap());
assert_eq!(EthVersion::Eth68, EthVersion::try_from("68").unwrap());
assert_eq!(EthVersion::Eth69, EthVersion::try_from("69").unwrap());
assert_eq!(EthVersion::Eth70, EthVersion::try_from("70").unwrap());
assert_eq!(Err(ParseVersionError("70".to_string())), EthVersion::try_from("70"));
}
#[test]
@@ -224,18 +214,12 @@ mod tests {
assert_eq!(EthVersion::Eth67, "67".parse().unwrap());
assert_eq!(EthVersion::Eth68, "68".parse().unwrap());
assert_eq!(EthVersion::Eth69, "69".parse().unwrap());
assert_eq!(EthVersion::Eth70, "70".parse().unwrap());
assert_eq!(Err(ParseVersionError("70".to_string())), "70".parse::<EthVersion>());
}
#[test]
fn test_eth_version_rlp_encode() {
let versions = [
EthVersion::Eth66,
EthVersion::Eth67,
EthVersion::Eth68,
EthVersion::Eth69,
EthVersion::Eth70,
];
let versions = [EthVersion::Eth66, EthVersion::Eth67, EthVersion::Eth68, EthVersion::Eth69];
for version in versions {
let mut encoded = BytesMut::new();
@@ -252,7 +236,7 @@ mod tests {
(67_u8, Ok(EthVersion::Eth67)),
(68_u8, Ok(EthVersion::Eth68)),
(69_u8, Ok(EthVersion::Eth69)),
(70_u8, Ok(EthVersion::Eth70)),
(70_u8, Err(RlpError::Custom("invalid eth version"))),
(65_u8, Err(RlpError::Custom("invalid eth version"))),
];

View File

@@ -418,8 +418,6 @@ mod tests {
Capability::new_static("eth", 66),
Capability::new_static("eth", 67),
Capability::new_static("eth", 68),
Capability::new_static("eth", 69),
Capability::new_static("eth", 70),
]
.into();
@@ -427,8 +425,6 @@ mod tests {
assert!(capabilities.supports_eth_v66());
assert!(capabilities.supports_eth_v67());
assert!(capabilities.supports_eth_v68());
assert!(capabilities.supports_eth_v69());
assert!(capabilities.supports_eth_v70());
}
#[test]

View File

@@ -260,11 +260,10 @@ mod tests {
assert_eq!(hello_encoded.len(), hello.length());
}
//TODO: add test for eth70 here once we have fully support it
#[test]
fn test_default_protocols_still_include_eth69() {
// ensure that older eth/69 remains advertised for compatibility
fn test_default_protocols_include_eth69() {
// ensure that the default protocol list includes Eth69 as the latest version
let secret_key = SecretKey::new(&mut rand_08::thread_rng());
let id = pk2id(&secret_key.public_key(SECP256K1));
let hello = HelloMessageWithProtocols::builder(id).build();

View File

@@ -19,7 +19,7 @@ pub use net_if::{NetInterfaceError, DEFAULT_NET_IF_NAME};
use std::{
fmt,
future::{poll_fn, Future},
net::{AddrParseError, IpAddr, ToSocketAddrs},
net::{AddrParseError, IpAddr},
pin::Pin,
str::FromStr,
task::{Context, Poll},
@@ -38,7 +38,7 @@ const EXTERNAL_IP_APIS: &[&str] =
&["https://ipinfo.io/ip", "https://icanhazip.com", "https://ifconfig.me"];
/// All builtin resolvers.
#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Default, Hash)]
#[cfg_attr(feature = "serde", derive(SerializeDisplay, DeserializeFromStr))]
pub enum NatResolver {
/// Resolve with any available resolver.
@@ -50,14 +50,6 @@ pub enum NatResolver {
PublicIp,
/// Use the given [`IpAddr`]
ExternalIp(IpAddr),
/// Use the given domain name as the external address to expose to peers.
/// This is behaving essentially the same as [`NatResolver::ExternalIp`], but supports domain
/// names. Domain names are resolved to IP addresses using the OS's resolver. The first IP
/// address found is used.
/// This may be useful in docker bridge networks where containers are usually queried by DNS
/// instead of direct IP addresses.
/// Note: the domain shouldn't include a port number. Only the IP address is resolved.
ExternalAddr(String),
/// Resolve external IP via the network interface.
NetIf,
/// Resolve nothing
@@ -70,17 +62,10 @@ impl NatResolver {
external_addr_with(self).await
}
/// Returns the fixed ip, if it is [`NatResolver::ExternalIp`] or [`NatResolver::ExternalAddr`].
///
/// In the case of [`NatResolver::ExternalAddr`], it will return the first IP address found for
/// the domain.
pub fn as_external_ip(self, port: u16) -> Option<IpAddr> {
/// Returns the external ip, if it is [`NatResolver::ExternalIp`]
pub const fn as_external_ip(self) -> Option<IpAddr> {
match self {
Self::ExternalIp(ip) => Some(ip),
Self::ExternalAddr(domain) => format!("{domain}:{port}")
.to_socket_addrs()
.ok()
.and_then(|mut addrs| addrs.next().map(|addr| addr.ip())),
_ => None,
}
}
@@ -93,7 +78,6 @@ impl fmt::Display for NatResolver {
Self::Upnp => f.write_str("upnp"),
Self::PublicIp => f.write_str("publicip"),
Self::ExternalIp(ip) => write!(f, "extip:{ip}"),
Self::ExternalAddr(domain) => write!(f, "extaddr:{domain}"),
Self::NetIf => f.write_str("netif"),
Self::None => f.write_str("none"),
}
@@ -122,15 +106,12 @@ impl FromStr for NatResolver {
"publicip" | "public-ip" => Self::PublicIp,
"netif" => Self::NetIf,
s => {
if let Some(ip) = s.strip_prefix("extip:") {
Self::ExternalIp(ip.parse()?)
} else if let Some(domain) = s.strip_prefix("extaddr:") {
Self::ExternalAddr(domain.to_string())
} else {
let Some(ip) = s.strip_prefix("extip:") else {
return Err(ParseNatResolverError::UnknownVariant(format!(
"Unknown Nat Resolver: {s}"
)));
}
)))
};
Self::ExternalIp(ip.parse()?)
}
};
Ok(r)
@@ -199,7 +180,7 @@ impl ResolveNatInterval {
/// `None` if the attempt was unsuccessful.
pub fn poll_tick(&mut self, cx: &mut Context<'_>) -> Poll<Option<IpAddr>> {
if self.interval.poll_tick(cx).is_ready() {
self.future = Some(Box::pin(self.resolver.clone().external_addr()));
self.future = Some(Box::pin(self.resolver.external_addr()));
}
if let Some(mut fut) = self.future.take() {
@@ -231,9 +212,6 @@ pub async fn external_addr_with(resolver: NatResolver) -> Option<IpAddr> {
);
})
.ok(),
NatResolver::ExternalAddr(domain) => {
domain.to_socket_addrs().ok().and_then(|mut addrs| addrs.next().map(|addr| addr.ip()))
}
NatResolver::None => None,
}
}
@@ -267,7 +245,7 @@ async fn resolve_external_ip_url(url: &str) -> Option<IpAddr> {
#[cfg(test)]
mod tests {
use super::*;
use std::net::{Ipv4Addr, Ipv6Addr};
use std::net::Ipv4Addr;
#[tokio::test]
#[ignore]
@@ -289,18 +267,6 @@ mod tests {
dbg!(ip);
}
#[test]
fn as_external_ip_test() {
let resolver = NatResolver::ExternalAddr("localhost".to_string());
let ip = resolver.as_external_ip(30303).expect("localhost should be resolvable");
if ip.is_ipv4() {
assert_eq!(ip, IpAddr::V4(Ipv4Addr::LOCALHOST));
} else {
assert_eq!(ip, IpAddr::V6(Ipv6Addr::LOCALHOST));
}
}
#[test]
fn test_from_str() {
assert_eq!(NatResolver::Any, "any".parse().unwrap());
@@ -309,6 +275,6 @@ mod tests {
let ip = NatResolver::ExternalIp(IpAddr::V4(Ipv4Addr::UNSPECIFIED));
let s = "extip:0.0.0.0";
assert_eq!(ip, s.parse().unwrap());
assert_eq!(ip.to_string(), s);
assert_eq!(ip.to_string().as_str(), s);
}
}

View File

@@ -3,8 +3,8 @@
use reth_eth_wire_types::{
message::RequestPair, BlockBodies, BlockHeaders, Capabilities, DisconnectReason, EthMessage,
EthNetworkPrimitives, EthVersion, GetBlockBodies, GetBlockHeaders, GetNodeData,
GetPooledTransactions, GetReceipts, GetReceipts70, NetworkPrimitives, NodeData,
PooledTransactions, Receipts, Receipts69, Receipts70, UnifiedStatus,
GetPooledTransactions, GetReceipts, NetworkPrimitives, NodeData, PooledTransactions, Receipts,
Receipts69, UnifiedStatus,
};
use reth_ethereum_forks::ForkId;
use reth_network_p2p::error::{RequestError, RequestResult};
@@ -238,15 +238,6 @@ pub enum PeerRequest<N: NetworkPrimitives = EthNetworkPrimitives> {
/// The channel to send the response for receipts.
response: oneshot::Sender<RequestResult<Receipts69<N::Receipt>>>,
},
/// Requests receipts from the peer using eth/70 (supports `firstBlockReceiptIndex`).
///
/// The response should be sent through the channel.
GetReceipts70 {
/// The request for receipts.
request: GetReceipts70,
/// The channel to send the response for receipts.
response: oneshot::Sender<RequestResult<Receipts70<N::Receipt>>>,
},
}
// === impl PeerRequest ===
@@ -266,7 +257,6 @@ impl<N: NetworkPrimitives> PeerRequest<N> {
Self::GetNodeData { response, .. } => response.send(Err(err)).ok(),
Self::GetReceipts { response, .. } => response.send(Err(err)).ok(),
Self::GetReceipts69 { response, .. } => response.send(Err(err)).ok(),
Self::GetReceipts70 { response, .. } => response.send(Err(err)).ok(),
};
}
@@ -291,9 +281,6 @@ impl<N: NetworkPrimitives> PeerRequest<N> {
Self::GetReceipts { request, .. } | Self::GetReceipts69 { request, .. } => {
EthMessage::GetReceipts(RequestPair { request_id, message: request.clone() })
}
Self::GetReceipts70 { request, .. } => {
EthMessage::GetReceipts70(RequestPair { request_id, message: request.clone() })
}
}
}

View File

@@ -66,7 +66,6 @@ tracing.workspace = true
rustc-hash.workspace = true
thiserror.workspace = true
parking_lot.workspace = true
rayon.workspace = true
rand.workspace = true
rand_08.workspace = true
secp256k1 = { workspace = true, features = ["global-context", "std", "recovery"] }

View File

@@ -433,7 +433,7 @@ impl<N: NetworkPrimitives> NetworkConfigBuilder<N> {
pub fn external_ip_resolver(mut self, resolver: NatResolver) -> Self {
self.discovery_v4_builder
.get_or_insert_with(Discv4Config::builder)
.external_ip_resolver(Some(resolver.clone()));
.external_ip_resolver(Some(resolver));
self.nat = Some(resolver);
self
}
@@ -484,7 +484,7 @@ impl<N: NetworkPrimitives> NetworkConfigBuilder<N> {
}
// Disable nat
pub fn disable_nat(mut self) -> Self {
pub const fn disable_nat(mut self) -> Self {
self.nat = None;
self
}
@@ -579,7 +579,7 @@ impl<N: NetworkPrimitives> NetworkConfigBuilder<N> {
}
/// Sets the NAT resolver for external IP.
pub fn add_nat(mut self, nat: Option<NatResolver>) -> Self {
pub const fn add_nat(mut self, nat: Option<NatResolver>) -> Self {
self.nat = nat;
self
}

View File

@@ -10,8 +10,7 @@ use alloy_rlp::Encodable;
use futures::StreamExt;
use reth_eth_wire::{
BlockBodies, BlockHeaders, EthNetworkPrimitives, GetBlockBodies, GetBlockHeaders, GetNodeData,
GetReceipts, GetReceipts70, HeadersDirection, NetworkPrimitives, NodeData, Receipts,
Receipts69, Receipts70,
GetReceipts, HeadersDirection, NetworkPrimitives, NodeData, Receipts, Receipts69,
};
use reth_network_api::test_utils::PeersHandle;
use reth_network_p2p::error::RequestResult;
@@ -218,69 +217,6 @@ where
let _ = response.send(Ok(Receipts69(receipts)));
}
/// Handles partial responses for [`GetReceipts70`] queries.
///
/// This will adhere to the soft limit but allow filling the last vec partially.
fn on_receipts70_request(
&self,
_peer_id: PeerId,
request: GetReceipts70,
response: oneshot::Sender<RequestResult<Receipts70<C::Receipt>>>,
) {
self.metrics.eth_receipts_requests_received_total.increment(1);
let GetReceipts70 { first_block_receipt_index, block_hashes } = request;
let mut receipts = Vec::new();
let mut total_bytes = 0usize;
let mut last_block_incomplete = false;
for (idx, hash) in block_hashes.into_iter().enumerate() {
if idx >= MAX_RECEIPTS_SERVE {
break
}
let Some(mut block_receipts) =
self.client.receipts_by_block(BlockHashOrNumber::Hash(hash)).unwrap_or_default()
else {
break
};
if idx == 0 && first_block_receipt_index > 0 {
let skip = first_block_receipt_index as usize;
if skip >= block_receipts.len() {
block_receipts.clear();
} else {
block_receipts.drain(0..skip);
}
}
let block_size = block_receipts.length();
if total_bytes + block_size <= SOFT_RESPONSE_LIMIT {
total_bytes += block_size;
receipts.push(block_receipts);
continue;
}
let mut partial_block = Vec::new();
for receipt in block_receipts {
let receipt_size = receipt.length();
if total_bytes + receipt_size > SOFT_RESPONSE_LIMIT {
break;
}
total_bytes += receipt_size;
partial_block.push(receipt);
}
receipts.push(partial_block);
last_block_incomplete = true;
break;
}
let _ = response.send(Ok(Receipts70 { last_block_incomplete, receipts }));
}
#[inline]
fn get_receipts_response<T, F>(&self, request: GetReceipts, transform_fn: F) -> Vec<Vec<T>>
where
@@ -349,9 +285,6 @@ where
IncomingEthRequest::GetReceipts69 { peer_id, request, response } => {
this.on_receipts69_request(peer_id, request, response)
}
IncomingEthRequest::GetReceipts70 { peer_id, request, response } => {
this.on_receipts70_request(peer_id, request, response)
}
}
},
);
@@ -426,15 +359,4 @@ pub enum IncomingEthRequest<N: NetworkPrimitives = EthNetworkPrimitives> {
/// The channel sender for the response containing Receipts69.
response: oneshot::Sender<RequestResult<Receipts69<N::Receipt>>>,
},
/// Request Receipts from the peer using eth/70.
///
/// The response should be sent through the channel.
GetReceipts70 {
/// The ID of the peer to request receipts from.
peer_id: PeerId,
/// The specific receipts requested including the `firstBlockReceiptIndex`.
request: GetReceipts70,
/// The channel sender for the response containing Receipts70.
response: oneshot::Sender<RequestResult<Receipts70<N::Receipt>>>,
},
}

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