mirror of
https://github.com/paradigmxyz/reth.git
synced 2026-04-30 03:01:58 -04:00
Compare commits
6 Commits
yk/worker-
...
v1.9.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74351d98e9 | ||
|
|
a672700b4f | ||
|
|
465d7479a7 | ||
|
|
5b3cb2d101 | ||
|
|
3afe69a573 | ||
|
|
35ac40a70b |
@@ -15,12 +15,3 @@ slow-timeout = { period = "2m", terminate-after = 10 }
|
||||
[[profile.default.overrides]]
|
||||
filter = "binary(e2e_testsuite)"
|
||||
slow-timeout = { period = "2m", terminate-after = 3 }
|
||||
|
||||
[[profile.default.overrides]]
|
||||
filter = "package(reth-era) and binary(it)"
|
||||
slow-timeout = { period = "2m", terminate-after = 10 }
|
||||
|
||||
# Allow slower ethereum node e2e tests (p2p + blobs) to run up to 5 minutes.
|
||||
[[profile.default.overrides]]
|
||||
filter = "package(reth-node-ethereum) and binary(e2e)"
|
||||
slow-timeout = { period = "1m", terminate-after = 5 }
|
||||
|
||||
@@ -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",
|
||||
"--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",
|
||||
# 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.
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
# include source files
|
||||
!/bin
|
||||
!/crates
|
||||
!/pkg
|
||||
!/testing
|
||||
!book.toml
|
||||
!Cargo.lock
|
||||
@@ -12,7 +11,6 @@
|
||||
!Cross.toml
|
||||
!deny.toml
|
||||
!Makefile
|
||||
!README.md
|
||||
|
||||
# include for vergen constants
|
||||
!/.git
|
||||
|
||||
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@@ -40,6 +40,5 @@ crates/tasks/ @mattsse
|
||||
crates/tokio-util/ @fgimenez
|
||||
crates/transaction-pool/ @mattsse @yongkangc
|
||||
crates/trie/ @Rjected @shekhirin @mediocregopher
|
||||
bin/reth-bench-compare/ @mediocregopher @shekhirin @yongkangc
|
||||
etc/ @Rjected @shekhirin
|
||||
.github/ @gakonst @DaniPopes
|
||||
|
||||
5
.github/workflows/bench.yml
vendored
5
.github/workflows/bench.yml
vendored
@@ -15,9 +15,10 @@ env:
|
||||
name: bench
|
||||
jobs:
|
||||
codspeed:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
submodules: true
|
||||
- uses: rui314/setup-mold@v1
|
||||
|
||||
2
.github/workflows/book.yml
vendored
2
.github/workflows/book.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
|
||||
7
.github/workflows/compact.yml
vendored
7
.github/workflows/compact.yml
vendored
@@ -17,7 +17,8 @@ env:
|
||||
name: compact-codec
|
||||
jobs:
|
||||
compact-codec:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
strategy:
|
||||
matrix:
|
||||
bin:
|
||||
@@ -30,7 +31,7 @@ jobs:
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- name: Checkout base
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
ref: ${{ github.base_ref || 'main' }}
|
||||
# On `main` branch, generates test vectors and serializes them to disk using `Compact`.
|
||||
@@ -38,7 +39,7 @@ jobs:
|
||||
run: |
|
||||
${{ matrix.bin }} -- test-vectors compact --write
|
||||
- name: Checkout PR
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
clean: false
|
||||
# On incoming merge try to read and decode previously generated vectors with `Compact`
|
||||
|
||||
2
.github/workflows/docker-git.yml
vendored
2
.github/workflows/docker-git.yml
vendored
@@ -33,7 +33,7 @@ jobs:
|
||||
- name: 'Build and push the git-sha-tagged op-reth image'
|
||||
command: 'make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME GIT_SHA=$GIT_SHA PROFILE=maxperf op-docker-build-push-git-sha'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
2
.github/workflows/docker-nightly.yml
vendored
2
.github/workflows/docker-nightly.yml
vendored
@@ -35,7 +35,7 @@ jobs:
|
||||
- name: 'Build and push the nightly profiling op-reth image'
|
||||
command: 'make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=profiling op-docker-build-push-nightly-profiling'
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- name: Remove bloatware
|
||||
uses: laverdet/remove-bloatware@v1.0.0
|
||||
with:
|
||||
|
||||
73
.github/workflows/docker-tag-latest.yml
vendored
73
.github/workflows/docker-tag-latest.yml
vendored
@@ -1,73 +0,0 @@
|
||||
# Tag a specific Docker release version as latest
|
||||
|
||||
name: docker-tag-latest
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Release version to tag as latest (e.g., v1.8.4)'
|
||||
required: true
|
||||
type: string
|
||||
tag_reth:
|
||||
description: 'Tag reth image as latest'
|
||||
required: false
|
||||
type: boolean
|
||||
default: true
|
||||
tag_op_reth:
|
||||
description: 'Tag op-reth image as latest'
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
env:
|
||||
DOCKER_USERNAME: ${{ github.actor }}
|
||||
|
||||
jobs:
|
||||
tag-reth-latest:
|
||||
name: Tag reth as latest
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ inputs.tag_reth }}
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Log in to Docker
|
||||
run: |
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io --username ${DOCKER_USERNAME} --password-stdin
|
||||
|
||||
- name: Pull reth release image
|
||||
run: |
|
||||
docker pull ghcr.io/${{ github.repository_owner }}/reth:${{ inputs.version }}
|
||||
|
||||
- name: Tag reth as latest
|
||||
run: |
|
||||
docker tag ghcr.io/${{ github.repository_owner }}/reth:${{ inputs.version }} ghcr.io/${{ github.repository_owner }}/reth:latest
|
||||
|
||||
- name: Push reth latest tag
|
||||
run: |
|
||||
docker push ghcr.io/${{ github.repository_owner }}/reth:latest
|
||||
|
||||
tag-op-reth-latest:
|
||||
name: Tag op-reth as latest
|
||||
runs-on: ubuntu-24.04
|
||||
if: ${{ inputs.tag_op_reth }}
|
||||
permissions:
|
||||
packages: write
|
||||
contents: read
|
||||
steps:
|
||||
- name: Log in to Docker
|
||||
run: |
|
||||
echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io --username ${DOCKER_USERNAME} --password-stdin
|
||||
|
||||
- name: Pull op-reth release image
|
||||
run: |
|
||||
docker pull ghcr.io/${{ github.repository_owner }}/op-reth:${{ inputs.version }}
|
||||
|
||||
- name: Tag op-reth as latest
|
||||
run: |
|
||||
docker tag ghcr.io/${{ github.repository_owner }}/op-reth:${{ inputs.version }} ghcr.io/${{ github.repository_owner }}/op-reth:latest
|
||||
|
||||
- name: Push op-reth latest tag
|
||||
run: |
|
||||
docker push ghcr.io/${{ github.repository_owner }}/op-reth:latest
|
||||
4
.github/workflows/docker.yml
vendored
4
.github/workflows/docker.yml
vendored
@@ -32,7 +32,7 @@ jobs:
|
||||
- name: "Build and push op-reth image"
|
||||
command: "make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
- name: "Build and push op-reth image"
|
||||
command: "make IMAGE_NAME=$OP_IMAGE_NAME DOCKER_IMAGE_NAME=$OP_DOCKER_IMAGE_NAME PROFILE=maxperf op-docker-build-push-latest"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
6
.github/workflows/e2e.yml
vendored
6
.github/workflows/e2e.yml
vendored
@@ -19,12 +19,13 @@ concurrency:
|
||||
jobs:
|
||||
test:
|
||||
name: e2e-testsuite
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@nextest
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -42,3 +43,4 @@ jobs:
|
||||
--exclude 'op-reth' \
|
||||
--exclude 'reth' \
|
||||
-E 'binary(e2e_testsuite)'
|
||||
|
||||
|
||||
21
.github/workflows/grafana.yml
vendored
21
.github/workflows/grafana.yml
vendored
@@ -1,21 +0,0 @@
|
||||
name: grafana
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
merge_group:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
check-dashboard:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- name: Check for ${DS_PROMETHEUS} in overview.json
|
||||
run: |
|
||||
if grep -Fn '${DS_PROMETHEUS}' etc/grafana/dashboards/overview.json; then
|
||||
echo "Error: overview.json contains '\${DS_PROMETHEUS}' placeholder"
|
||||
echo "Please replace it with '\${datasource}'"
|
||||
exit 1
|
||||
fi
|
||||
echo "✓ overview.json does not contain '\${DS_PROMETHEUS}' placeholder"
|
||||
17
.github/workflows/hive.yml
vendored
17
.github/workflows/hive.yml
vendored
@@ -24,11 +24,12 @@ jobs:
|
||||
prepare-hive:
|
||||
if: github.repository == 'paradigmxyz/reth'
|
||||
timeout-minutes: 45
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- name: Checkout hive tests
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ethereum/hive
|
||||
path: hivetests
|
||||
@@ -178,11 +179,12 @@ jobs:
|
||||
- prepare-reth
|
||||
- prepare-hive
|
||||
name: run ${{ matrix.scenario.sim }}${{ matrix.scenario.limit && format(' - {0}', matrix.scenario.limit) }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
permissions:
|
||||
issues: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -207,7 +209,7 @@ jobs:
|
||||
chmod +x /usr/local/bin/hive
|
||||
|
||||
- name: Checkout hive tests
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ethereum/hive
|
||||
ref: master
|
||||
@@ -245,7 +247,8 @@ jobs:
|
||||
notify-on-error:
|
||||
needs: test
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
steps:
|
||||
- name: Slack Webhook Action
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
|
||||
9
.github/workflows/integration.yml
vendored
9
.github/workflows/integration.yml
vendored
@@ -23,7 +23,8 @@ jobs:
|
||||
test:
|
||||
name: test / ${{ matrix.network }}
|
||||
if: github.event_name != 'schedule'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
strategy:
|
||||
@@ -31,7 +32,7 @@ jobs:
|
||||
network: ["ethereum", "optimism"]
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Install Geth
|
||||
@@ -70,7 +71,7 @@ jobs:
|
||||
if: github.event_name == 'schedule'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@nextest
|
||||
@@ -78,4 +79,4 @@ jobs:
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- name: run era1 files integration tests
|
||||
run: cargo nextest run --release --package reth-era --test it -- --ignored
|
||||
run: cargo nextest run --package reth-era --test it -- --ignored
|
||||
|
||||
11
.github/workflows/kurtosis-op.yml
vendored
11
.github/workflows/kurtosis-op.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
- '*'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -32,11 +32,12 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
name: run kurtosis
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
needs:
|
||||
- prepare-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -82,10 +83,12 @@ jobs:
|
||||
kurtosis service logs -a op-devnet op-cl-2151908-2-op-node-op-reth-op-kurtosis
|
||||
exit 1
|
||||
|
||||
|
||||
notify-on-error:
|
||||
needs: test
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
steps:
|
||||
- name: Slack Webhook Action
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
|
||||
12
.github/workflows/kurtosis.yml
vendored
12
.github/workflows/kurtosis.yml
vendored
@@ -9,7 +9,7 @@ on:
|
||||
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
- '*'
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
@@ -30,11 +30,12 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
name: run kurtosis
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
needs:
|
||||
- prepare-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
@@ -53,12 +54,13 @@ jobs:
|
||||
- name: Run kurtosis
|
||||
uses: ethpandaops/kurtosis-assertoor-github-action@v1
|
||||
with:
|
||||
ethereum_package_args: ".github/assets/kurtosis_network_params.yaml"
|
||||
ethereum_package_args: '.github/assets/kurtosis_network_params.yaml'
|
||||
|
||||
notify-on-error:
|
||||
needs: test
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
steps:
|
||||
- name: Slack Webhook Action
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
|
||||
2
.github/workflows/label-pr.yml
vendored
2
.github/workflows/label-pr.yml
vendored
@@ -11,7 +11,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
|
||||
2
.github/workflows/lint-actions.yml
vendored
2
.github/workflows/lint-actions.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
actionlint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- name: Download actionlint
|
||||
id: get_actionlint
|
||||
run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash)
|
||||
|
||||
46
.github/workflows/lint.yml
vendored
46
.github/workflows/lint.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
args: --workspace --lib --examples --tests --benches --locked
|
||||
features: "ethereum asm-keccak jemalloc jemalloc-prof min-error-logs min-warn-logs min-info-logs min-debug-logs min-trace-logs"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@clippy
|
||||
with:
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
@@ -59,7 +59,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -78,7 +78,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -92,22 +92,17 @@ jobs:
|
||||
run: .github/assets/check_rv32imac.sh
|
||||
|
||||
crate-checks:
|
||||
name: crate-checks (${{ matrix.partition }}/${{ matrix.total_partitions }})
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
partition: [1, 2]
|
||||
total_partitions: [2]
|
||||
timeout-minutes: 60
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- run: cargo hack check --workspace --partition ${{ matrix.partition }}/${{ matrix.total_partitions }}
|
||||
- run: cargo hack check --workspace
|
||||
|
||||
msrv:
|
||||
name: MSRV
|
||||
@@ -119,7 +114,7 @@ jobs:
|
||||
- binary: reth
|
||||
- binary: op-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
@@ -136,7 +131,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -153,7 +148,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
with:
|
||||
@@ -166,7 +161,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -180,17 +175,16 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@nightly
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
cache-on-failure: true
|
||||
- run: cargo build --bin reth --workspace
|
||||
- run: cargo build --bin op-reth --workspace
|
||||
- run: cargo build --bin reth --workspace --features ethereum
|
||||
env:
|
||||
RUSTFLAGS: -D warnings
|
||||
- run: ./docs/cli/update.sh target/debug/reth target/debug/op-reth
|
||||
- run: ./docs/cli/update.sh target/debug/reth
|
||||
- name: Check docs changes
|
||||
run: git diff --exit-code
|
||||
|
||||
@@ -198,7 +192,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: crate-ci/typos@v1
|
||||
|
||||
check-toml:
|
||||
@@ -206,7 +200,7 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
- name: Run dprint
|
||||
uses: dprint/check@v2.3
|
||||
with:
|
||||
@@ -216,7 +210,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- name: Check dashboard JSON with jq
|
||||
uses: sergeysova/jq-action@v2
|
||||
with:
|
||||
@@ -226,7 +220,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Ensure no arbitrary or proptest dependency on default build
|
||||
@@ -238,7 +232,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -255,7 +249,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: taiki-e/cache-cargo-install-action@v2
|
||||
|
||||
5
.github/workflows/prepare-reth.yml
vendored
5
.github/workflows/prepare-reth.yml
vendored
@@ -26,9 +26,10 @@ jobs:
|
||||
prepare-reth:
|
||||
if: github.repository == 'paradigmxyz/reth'
|
||||
timeout-minutes: 45
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- run: mkdir artifacts
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
|
||||
2
.github/workflows/release-dist.yml
vendored
2
.github/workflows/release-dist.yml
vendored
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Update Homebrew formula
|
||||
uses: dawidd6/action-homebrew-bump-formula@v6
|
||||
uses: dawidd6/action-homebrew-bump-formula@v5
|
||||
with:
|
||||
token: ${{ secrets.HOMEBREW }}
|
||||
no_fork: true
|
||||
|
||||
92
.github/workflows/release-reproducible.yml
vendored
92
.github/workflows/release-reproducible.yml
vendored
@@ -1,11 +1,11 @@
|
||||
# This workflow is for building and pushing reproducible artifacts for releases
|
||||
# This workflow is for building and pushing reproducible Docker images for releases.
|
||||
|
||||
name: release-reproducible
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: [release]
|
||||
types: [completed]
|
||||
push:
|
||||
tags:
|
||||
- v*
|
||||
|
||||
env:
|
||||
DOCKER_REPRODUCIBLE_IMAGE_NAME: ghcr.io/${{ github.repository_owner }}/reth-reproducible
|
||||
@@ -13,41 +13,23 @@ env:
|
||||
jobs:
|
||||
extract-version:
|
||||
name: extract version
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Extract version from triggering tag
|
||||
- name: Extract version
|
||||
run: echo "VERSION=$(echo ${GITHUB_REF#refs/tags/})" >> $GITHUB_OUTPUT
|
||||
id: extract_version
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
# Get the tag that points to the head SHA of the triggering workflow
|
||||
TAG=$(gh api /repos/${{ github.repository }}/git/refs/tags \
|
||||
--jq '.[] | select(.object.sha == "${{ github.event.workflow_run.head_sha }}") | .ref' \
|
||||
| head -1 \
|
||||
| sed 's|refs/tags/||')
|
||||
|
||||
if [ -z "$TAG" ]; then
|
||||
echo "No tag found for SHA ${{ github.event.workflow_run.head_sha }}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "VERSION=$TAG" >> $GITHUB_OUTPUT
|
||||
outputs:
|
||||
VERSION: ${{ steps.extract_version.outputs.VERSION }}
|
||||
|
||||
build-reproducible:
|
||||
name: build and push reproducible image and binaries
|
||||
name: build and push reproducible image
|
||||
runs-on: ubuntu-latest
|
||||
needs: [extract-version]
|
||||
needs: extract-version
|
||||
permissions:
|
||||
packages: write
|
||||
contents: write
|
||||
contents: read
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
ref: ${{ needs.extract-version.outputs.VERSION }}
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
@@ -58,37 +40,12 @@ jobs:
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Extract Rust version
|
||||
id: rust_version
|
||||
run: |
|
||||
RUST_TOOLCHAIN=$(rustc --version | cut -d' ' -f2)
|
||||
echo "RUST_TOOLCHAIN=$RUST_TOOLCHAIN" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Build reproducible artifacts
|
||||
uses: docker/build-push-action@v6
|
||||
id: docker_build
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.reproducible
|
||||
build-args: |
|
||||
RUST_TOOLCHAIN=${{ steps.rust_version.outputs.RUST_TOOLCHAIN }}
|
||||
VERSION=${{ needs.extract-version.outputs.VERSION }}
|
||||
target: artifacts
|
||||
outputs: type=local,dest=./docker-artifacts
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
env:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
|
||||
- name: Build and push final image
|
||||
- name: Build and push reproducible image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
file: ./Dockerfile.reproducible
|
||||
push: true
|
||||
build-args: |
|
||||
RUST_TOOLCHAIN=${{ steps.rust_version.outputs.RUST_TOOLCHAIN }}
|
||||
VERSION=${{ needs.extract-version.outputs.VERSION }}
|
||||
tags: |
|
||||
${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:${{ needs.extract-version.outputs.VERSION }}
|
||||
${{ env.DOCKER_REPRODUCIBLE_IMAGE_NAME }}:latest
|
||||
@@ -97,30 +54,3 @@ jobs:
|
||||
provenance: false
|
||||
env:
|
||||
DOCKER_BUILD_RECORD_UPLOAD: false
|
||||
|
||||
- name: Prepare artifacts from Docker build
|
||||
run: |
|
||||
mkdir reproducible-artifacts
|
||||
cp docker-artifacts/reth reproducible-artifacts/reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu
|
||||
cp docker-artifacts/*.deb reproducible-artifacts/reth-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu-reproducible.deb
|
||||
|
||||
- name: Configure GPG and create artifacts
|
||||
env:
|
||||
GPG_SIGNING_KEY: ${{ secrets.GPG_SIGNING_KEY }}
|
||||
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
|
||||
run: |
|
||||
export GPG_TTY=$(tty)
|
||||
echo -n "$GPG_SIGNING_KEY" | base64 --decode | gpg --batch --import
|
||||
|
||||
cd reproducible-artifacts
|
||||
tar -czf reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu.tar.gz reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu --remove-files
|
||||
echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab reth-reproducible-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu.tar.gz
|
||||
echo "$GPG_PASSPHRASE" | gpg --passphrase-fd 0 --pinentry-mode loopback --batch -ab reth-${{ needs.extract-version.outputs.VERSION }}-x86_64-unknown-linux-gnu-reproducible.deb
|
||||
|
||||
- name: Upload reproducible artifacts to release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
gh release upload ${{ needs.extract-version.outputs.VERSION }} \
|
||||
reproducible-artifacts/*
|
||||
|
||||
|
||||
6
.github/workflows/release.yml
vendored
6
.github/workflows/release.yml
vendored
@@ -49,7 +49,7 @@ jobs:
|
||||
needs: extract-version
|
||||
if: ${{ github.event.inputs.dry_run != 'true' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- name: Verify crate version matches tag
|
||||
# Check that the Cargo version starts with the tag,
|
||||
@@ -99,7 +99,7 @@ jobs:
|
||||
- command: op-build
|
||||
binary: op-reth
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -166,7 +166,7 @@ jobs:
|
||||
steps:
|
||||
# This is necessary for generating the changelog.
|
||||
# It has to come before "Download Artifacts" or else it deletes the artifacts.
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Download artifacts
|
||||
|
||||
82
.github/workflows/reproducible-build.yml
vendored
82
.github/workflows/reproducible-build.yml
vendored
@@ -8,73 +8,31 @@ on:
|
||||
jobs:
|
||||
build:
|
||||
name: build reproducible binaries
|
||||
runs-on: ${{ matrix.runner }}
|
||||
strategy:
|
||||
matrix:
|
||||
include:
|
||||
- runner: ubuntu-latest
|
||||
machine: machine-1
|
||||
- runner: ubuntu-22.04
|
||||
machine: machine-2
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
target: x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Build reproducible binary with Docker
|
||||
- name: Install cross main
|
||||
run: |
|
||||
RUST_TOOLCHAIN=$(rustc --version | cut -d' ' -f2)
|
||||
docker build \
|
||||
--build-arg "RUST_TOOLCHAIN=${RUST_TOOLCHAIN}" \
|
||||
-f Dockerfile.reproducible -t reth:release \
|
||||
--target artifacts \
|
||||
--output type=local,dest=./target .
|
||||
|
||||
- name: Calculate SHA256
|
||||
id: sha256
|
||||
cargo install cross --git https://github.com/cross-rs/cross
|
||||
- name: Install cargo-cache
|
||||
run: |
|
||||
sha256sum target/reth > checksum.sha256
|
||||
echo "Binaries SHA256 on ${{ matrix.machine }}: $(cat checksum.sha256)"
|
||||
|
||||
- name: Upload the hash
|
||||
uses: actions/upload-artifact@v5
|
||||
cargo install cargo-cache
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
name: checksum-${{ matrix.machine }}
|
||||
path: |
|
||||
checksum.sha256
|
||||
retention-days: 1
|
||||
|
||||
compare:
|
||||
name: compare reproducible binaries
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Download artifacts from machine-1
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: checksum-machine-1
|
||||
path: machine-1/
|
||||
- name: Download artifacts from machine-2
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: checksum-machine-2
|
||||
path: machine-2/
|
||||
- name: Compare SHA256 hashes
|
||||
cache-on-failure: true
|
||||
- name: Build Reth
|
||||
run: |
|
||||
echo "=== SHA256 Comparison ==="
|
||||
echo "Machine 1 hash:"
|
||||
cat machine-1/checksum.sha256
|
||||
echo "Machine 2 hash:"
|
||||
cat machine-2/checksum.sha256
|
||||
|
||||
if cmp -s machine-1/checksum.sha256 machine-2/checksum.sha256; then
|
||||
echo "✅ SUCCESS: Binaries are identical (reproducible build verified)"
|
||||
else
|
||||
echo "❌ FAILURE: Binaries differ (reproducible build failed)"
|
||||
exit 1
|
||||
fi
|
||||
make build-reproducible
|
||||
mv target/x86_64-unknown-linux-gnu/release/reth reth-build-1
|
||||
- name: Clean cache
|
||||
run: make clean && cargo cache -a
|
||||
- name: Build Reth again
|
||||
run: |
|
||||
make build-reproducible
|
||||
mv target/x86_64-unknown-linux-gnu/release/reth reth-build-2
|
||||
- name: Compare binaries
|
||||
run: cmp reth-build-1 reth-build-2
|
||||
|
||||
5
.github/workflows/stage.yml
vendored
5
.github/workflows/stage.yml
vendored
@@ -22,13 +22,14 @@ jobs:
|
||||
name: stage-run-test
|
||||
# Only run stage commands test in merge groups
|
||||
if: github.event_name == 'merge_group'
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
env:
|
||||
RUST_LOG: info,sync=error
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
7
.github/workflows/sync-era.yml
vendored
7
.github/workflows/sync-era.yml
vendored
@@ -17,7 +17,8 @@ concurrency:
|
||||
jobs:
|
||||
sync:
|
||||
name: sync (${{ matrix.chain.bin }})
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
env:
|
||||
RUST_LOG: info,sync=error
|
||||
RUST_BACKTRACE: 1
|
||||
@@ -38,7 +39,7 @@ jobs:
|
||||
block: 10000
|
||||
unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -63,4 +64,4 @@ jobs:
|
||||
${{ matrix.chain.bin }} stage unwind num-blocks 100 --chain ${{ matrix.chain.chain }}
|
||||
- name: Run stage unwind to block hash
|
||||
run: |
|
||||
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}
|
||||
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}
|
||||
|
||||
7
.github/workflows/sync.yml
vendored
7
.github/workflows/sync.yml
vendored
@@ -17,7 +17,8 @@ concurrency:
|
||||
jobs:
|
||||
sync:
|
||||
name: sync (${{ matrix.chain.bin }})
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
env:
|
||||
RUST_LOG: info,sync=error
|
||||
RUST_BACKTRACE: 1
|
||||
@@ -38,7 +39,7 @@ jobs:
|
||||
block: 10000
|
||||
unwind-target: "0x118a6e922a8c6cab221fc5adfe5056d2b72d58c6580e9c5629de55299e2cf8de"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -62,4 +63,4 @@ jobs:
|
||||
${{ matrix.chain.bin }} stage unwind num-blocks 100 --chain ${{ matrix.chain.chain }}
|
||||
- name: Run stage unwind to block hash
|
||||
run: |
|
||||
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}
|
||||
${{ matrix.chain.bin }} stage unwind to-block ${{ matrix.chain.unwind-target }} --chain ${{ matrix.chain.chain }}
|
||||
|
||||
17
.github/workflows/unit.yml
vendored
17
.github/workflows/unit.yml
vendored
@@ -19,7 +19,8 @@ concurrency:
|
||||
jobs:
|
||||
test:
|
||||
name: test / ${{ matrix.type }} (${{ matrix.partition }}/${{ matrix.total_partitions }})
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
strategy:
|
||||
@@ -43,7 +44,7 @@ jobs:
|
||||
total_partitions: 2
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
@@ -64,15 +65,16 @@ jobs:
|
||||
|
||||
state:
|
||||
name: Ethereum state tests
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
env:
|
||||
RUST_LOG: info,sync=error
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- name: Checkout ethereum/tests
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
with:
|
||||
repository: ethereum/tests
|
||||
ref: 81862e4848585a438d64f911a19b3825f0f4cd95
|
||||
@@ -98,12 +100,13 @@ jobs:
|
||||
|
||||
doc:
|
||||
name: doc tests
|
||||
runs-on: ubuntu-latest
|
||||
runs-on:
|
||||
group: Reth
|
||||
env:
|
||||
RUST_BACKTRACE: 1
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
|
||||
2
.github/workflows/update-superchain.yml
vendored
2
.github/workflows/update-superchain.yml
vendored
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v5
|
||||
|
||||
- name: Install required tools
|
||||
run: |
|
||||
|
||||
4
.github/workflows/windows.yml
vendored
4
.github/workflows/windows.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
timeout-minutes: 60
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v5
|
||||
- uses: rui314/setup-mold@v1
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
|
||||
1140
Cargo.lock
generated
1140
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
98
Cargo.toml
98
Cargo.toml
@@ -1,5 +1,5 @@
|
||||
[workspace.package]
|
||||
version = "1.9.3"
|
||||
version = "1.9.2"
|
||||
edition = "2024"
|
||||
rust-version = "1.88"
|
||||
license = "MIT OR Apache-2.0"
|
||||
@@ -328,12 +328,6 @@ inherits = "release"
|
||||
lto = "fat"
|
||||
codegen-units = 1
|
||||
|
||||
[profile.reproducible]
|
||||
inherits = "release"
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
incremental = false
|
||||
|
||||
[workspace.dependencies]
|
||||
# reth
|
||||
op-reth = { path = "crates/optimism/bin" }
|
||||
@@ -472,65 +466,68 @@ reth-ress-protocol = { path = "crates/ress/protocol" }
|
||||
reth-ress-provider = { path = "crates/ress/provider" }
|
||||
|
||||
# revm
|
||||
revm = { version = "33.1.0", default-features = false }
|
||||
revm = { version = "31.0.2", default-features = false }
|
||||
revm-bytecode = { version = "7.1.1", default-features = false }
|
||||
revm-database = { version = "9.0.5", default-features = false }
|
||||
revm-state = { version = "8.1.1", default-features = false }
|
||||
revm-primitives = { version = "21.0.2", default-features = false }
|
||||
revm-interpreter = { version = "31.1.0", default-features = false }
|
||||
revm-interpreter = { version = "29.0.1", default-features = false }
|
||||
revm-inspector = { version = "12.0.2", default-features = false }
|
||||
revm-context = { version = "11.0.2", default-features = false }
|
||||
revm-context-interface = { version = "12.0.1", default-features = false }
|
||||
revm-database-interface = { version = "8.0.5", default-features = false }
|
||||
op-revm = { version = "14.1.0", default-features = false }
|
||||
revm-inspectors = "0.33.1"
|
||||
op-revm = { version = "12.0.2", default-features = false }
|
||||
revm-inspectors = "0.32.0"
|
||||
|
||||
# eth
|
||||
alloy-chains = { version = "0.2.5", default-features = false }
|
||||
alloy-dyn-abi = "1.4.1"
|
||||
alloy-eip2124 = { version = "0.2.0", default-features = false }
|
||||
alloy-evm = { version = "0.24.1", default-features = false }
|
||||
alloy-evm = { version = "0.23.0", default-features = false }
|
||||
alloy-primitives = { version = "1.4.1", default-features = false, features = ["map-foldhash"] }
|
||||
alloy-rlp = { version = "0.3.10", default-features = false, features = ["core-net"] }
|
||||
alloy-sol-macro = "1.4.1"
|
||||
alloy-sol-types = { version = "1.4.1", default-features = false }
|
||||
alloy-trie = { version = "0.9.1", default-features = false }
|
||||
|
||||
alloy-hardforks = "0.4.5"
|
||||
alloy-hardforks = "0.4.4"
|
||||
|
||||
alloy-consensus = { version = "1.1.3", default-features = false }
|
||||
alloy-contract = { version = "1.1.3", default-features = false }
|
||||
alloy-eips = { version = "1.1.3", default-features = false }
|
||||
alloy-genesis = { version = "1.1.3", default-features = false }
|
||||
alloy-json-rpc = { version = "1.1.3", default-features = false }
|
||||
alloy-network = { version = "1.1.3", default-features = false }
|
||||
alloy-network-primitives = { version = "1.1.3", default-features = false }
|
||||
alloy-provider = { version = "1.1.3", features = ["reqwest", "debug-api"], default-features = false }
|
||||
alloy-pubsub = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-client = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types = { version = "1.1.3", features = ["eth"], default-features = false }
|
||||
alloy-rpc-types-admin = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-anvil = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-beacon = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-debug = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-engine = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-eth = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-mev = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-trace = { version = "1.1.3", default-features = false }
|
||||
alloy-rpc-types-txpool = { version = "1.1.3", default-features = false }
|
||||
alloy-serde = { version = "1.1.3", default-features = false }
|
||||
alloy-signer = { version = "1.1.3", default-features = false }
|
||||
alloy-signer-local = { version = "1.1.3", default-features = false }
|
||||
alloy-transport = { version = "1.1.3" }
|
||||
alloy-transport-http = { version = "1.1.3", features = ["reqwest-rustls-tls"], default-features = false }
|
||||
alloy-transport-ipc = { version = "1.1.3", default-features = false }
|
||||
alloy-transport-ws = { version = "1.1.3", default-features = false }
|
||||
alloy-consensus = { version = "1.0.41", default-features = false }
|
||||
alloy-contract = { version = "1.0.41", default-features = false }
|
||||
alloy-eips = { version = "1.0.41", default-features = false }
|
||||
alloy-genesis = { version = "1.0.41", default-features = false }
|
||||
alloy-json-rpc = { version = "1.0.41", default-features = false }
|
||||
alloy-network = { version = "1.0.41", default-features = false }
|
||||
alloy-network-primitives = { version = "1.0.41", default-features = false }
|
||||
alloy-provider = { version = "1.0.41", features = ["reqwest"], default-features = false }
|
||||
alloy-pubsub = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-client = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-types = { version = "1.0.41", features = ["eth"], default-features = false }
|
||||
alloy-rpc-types-admin = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-types-anvil = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-types-beacon = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-types-debug = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-types-engine = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-types-eth = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-types-mev = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-types-trace = { version = "1.0.41", default-features = false }
|
||||
alloy-rpc-types-txpool = { version = "1.0.41", default-features = false }
|
||||
alloy-serde = { version = "1.0.41", default-features = false }
|
||||
alloy-signer = { version = "1.0.41", default-features = false }
|
||||
alloy-signer-local = { version = "1.0.41", default-features = false }
|
||||
alloy-transport = { version = "1.0.41" }
|
||||
alloy-transport-http = { version = "1.0.41", features = ["reqwest-rustls-tls"], default-features = false }
|
||||
alloy-transport-ipc = { version = "1.0.41", default-features = false }
|
||||
alloy-transport-ws = { version = "1.0.41", default-features = false }
|
||||
|
||||
# op
|
||||
alloy-op-evm = { version = "0.24.1", default-features = false }
|
||||
alloy-op-evm = { version = "0.23.0", default-features = false }
|
||||
alloy-op-hardforks = "0.4.4"
|
||||
op-alloy-rpc-types = { version = "0.22.4", default-features = false }
|
||||
op-alloy-rpc-types-engine = { version = "0.22.4", default-features = false }
|
||||
op-alloy-network = { version = "0.22.4", default-features = false }
|
||||
op-alloy-consensus = { version = "0.22.4", default-features = false }
|
||||
op-alloy-rpc-jsonrpsee = { version = "0.22.4", default-features = false }
|
||||
op-alloy-rpc-types = { version = "0.22.0", default-features = false }
|
||||
op-alloy-rpc-types-engine = { version = "0.22.0", default-features = false }
|
||||
op-alloy-network = { version = "0.22.0", default-features = false }
|
||||
op-alloy-consensus = { version = "0.22.0", default-features = false }
|
||||
op-alloy-rpc-jsonrpsee = { version = "0.22.0", default-features = false }
|
||||
op-alloy-flz = { version = "0.13.1", default-features = false }
|
||||
|
||||
# misc
|
||||
@@ -552,6 +549,8 @@ dirs-next = "2.0.0"
|
||||
dyn-clone = "1.0.17"
|
||||
eyre = "0.6"
|
||||
fdlimit = "0.3.0"
|
||||
# pinned until downstream crypto libs migrate to 1.0 because 0.14.8 marks all types as deprecated
|
||||
generic-array = "=0.14.7"
|
||||
humantime = "2.1"
|
||||
humantime-serde = "1.1"
|
||||
itertools = { version = "0.14", default-features = false }
|
||||
@@ -652,9 +651,6 @@ c-kzg = "2.1.5"
|
||||
# config
|
||||
toml = "0.8"
|
||||
|
||||
# rocksdb
|
||||
rocksdb = { version = "0.24" }
|
||||
|
||||
# otlp obs
|
||||
opentelemetry_sdk = "0.31"
|
||||
opentelemetry = "0.31"
|
||||
@@ -666,7 +662,6 @@ tracing-opentelemetry = "0.32"
|
||||
arbitrary = "1.3"
|
||||
assert_matches = "1.5.0"
|
||||
criterion = { package = "codspeed-criterion-compat", version = "2.7" }
|
||||
insta = "1.41"
|
||||
proptest = "1.7"
|
||||
proptest-derive = "0.5"
|
||||
similar-asserts = { version = "1.5.0", features = ["serde"] }
|
||||
@@ -735,9 +730,6 @@ visibility = "0.1.1"
|
||||
walkdir = "2.3.3"
|
||||
vergen-git2 = "1.0.5"
|
||||
|
||||
# networking
|
||||
ipnet = "2.11"
|
||||
|
||||
# [patch.crates-io]
|
||||
# alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
# alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
ARG RUST_TOOLCHAIN=1.89.0
|
||||
FROM docker.io/rust:$RUST_TOOLCHAIN-trixie AS builder
|
||||
# Use the Rust 1.88 image based on Debian Bookworm
|
||||
FROM rust:1.88-bookworm AS builder
|
||||
|
||||
ARG PROFILE
|
||||
ARG VERSION
|
||||
# Switch to snapshot repository to pin dependencies
|
||||
RUN sed -i '/^# http/{N;s|^# \(http[^ ]*\)\nURIs: .*|# \1\nURIs: \1|}' /etc/apt/sources.list.d/debian.sources
|
||||
RUN apt-get -o Acquire::Check-Valid-Until=false update && \
|
||||
apt-get install -y \
|
||||
libjemalloc-dev \
|
||||
libclang-dev \
|
||||
mold
|
||||
# Install specific version of libclang-dev
|
||||
RUN apt-get update && apt-get install -y libclang-dev=1:14.0-55.7~deb12u1
|
||||
|
||||
# Copy the project to the container
|
||||
COPY ./ /app
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN RUSTFLAGS_REPRODUCIBLE_EXTRA="-Clink-arg=-fuse-ld=mold" make build-reth-reproducible && \
|
||||
PROFILE=${PROFILE:-reproducible} VERSION=$VERSION make build-deb-x86_64-unknown-linux-gnu
|
||||
|
||||
FROM scratch AS artifacts
|
||||
COPY --from=builder /app/target/x86_64-unknown-linux-gnu/reproducible/reth /reth
|
||||
COPY --from=builder /app/target/x86_64-unknown-linux-gnu/reproducible/*.deb /
|
||||
# Build the project with the reproducible settings
|
||||
RUN make build-reproducible
|
||||
|
||||
FROM gcr.io/distroless/cc-debian13:nonroot-239cdd2c8a6b275b6a6f6ed1428c57de2fff3e50
|
||||
COPY --from=artifacts /reth /reth
|
||||
RUN mv /app/target/x86_64-unknown-linux-gnu/release/reth /reth
|
||||
|
||||
# Create a minimal final image with just the binary
|
||||
FROM gcr.io/distroless/cc-debian12:nonroot-6755e21ccd99ddead6edc8106ba03888cbeed41a
|
||||
COPY --from=builder /reth /reth
|
||||
EXPOSE 30303 30303/udp 9001 8545 8546
|
||||
ENTRYPOINT [ "/reth" ]
|
||||
|
||||
60
Makefile
60
Makefile
@@ -65,31 +65,37 @@ build: ## Build the reth binary into `target` directory.
|
||||
cargo build --bin reth --features "$(FEATURES)" --profile "$(PROFILE)"
|
||||
|
||||
# Environment variables for reproducible builds
|
||||
# Initialize RUSTFLAGS
|
||||
RUST_BUILD_FLAGS =
|
||||
# Enable static linking to ensure reproducibility across builds
|
||||
RUST_BUILD_FLAGS += --C target-feature=+crt-static
|
||||
# Set the linker to use static libgcc to ensure reproducibility across builds
|
||||
RUST_BUILD_FLAGS += -C link-arg=-static-libgcc
|
||||
# Remove build ID from the binary to ensure reproducibility across builds
|
||||
RUST_BUILD_FLAGS += -C link-arg=-Wl,--build-id=none
|
||||
# Remove metadata hash from symbol names to ensure reproducible builds
|
||||
RUST_BUILD_FLAGS += -C metadata=''
|
||||
# Set timestamp from last git commit for reproducible builds
|
||||
SOURCE_DATE ?= $(shell git log -1 --pretty=%ct)
|
||||
# Disable incremental compilation to avoid non-deterministic artifacts
|
||||
CARGO_INCREMENTAL_VAL = 0
|
||||
# Set C locale for consistent string handling and sorting
|
||||
LOCALE_VAL = C
|
||||
# Set UTC timezone for consistent time handling across builds
|
||||
TZ_VAL = UTC
|
||||
|
||||
# Extra RUSTFLAGS for reproducible builds. Can be overridden via the environment.
|
||||
RUSTFLAGS_REPRODUCIBLE_EXTRA ?=
|
||||
|
||||
# `reproducible` only supports reth on x86_64-unknown-linux-gnu
|
||||
build-%-reproducible:
|
||||
@if [ "$*" != "reth" ]; then \
|
||||
echo "Error: Reproducible builds are only supported for reth, not $*"; \
|
||||
exit 1; \
|
||||
fi
|
||||
.PHONY: build-reproducible
|
||||
build-reproducible: ## Build the reth binary into `target` directory with reproducible builds. Only works for x86_64-unknown-linux-gnu currently
|
||||
SOURCE_DATE_EPOCH=$(SOURCE_DATE) \
|
||||
RUSTFLAGS="-C symbol-mangling-version=v0 -C strip=none -C link-arg=-Wl,--build-id=none -C metadata='' --remap-path-prefix $$(pwd)=. $(RUSTFLAGS_REPRODUCIBLE_EXTRA)" \
|
||||
LC_ALL=C \
|
||||
TZ=UTC \
|
||||
JEMALLOC_OVERRIDE=/usr/lib/x86_64-linux-gnu/libjemalloc.a \
|
||||
cargo build --bin reth --features "$(FEATURES) jemalloc-unprefixed" --profile "reproducible" --locked --target x86_64-unknown-linux-gnu
|
||||
RUSTFLAGS="${RUST_BUILD_FLAGS} --remap-path-prefix $$(pwd)=." \
|
||||
CARGO_INCREMENTAL=${CARGO_INCREMENTAL_VAL} \
|
||||
LC_ALL=${LOCALE_VAL} \
|
||||
TZ=${TZ_VAL} \
|
||||
cargo build --bin reth --features "$(FEATURES)" --profile "release" --locked --target x86_64-unknown-linux-gnu
|
||||
|
||||
.PHONY: build-debug
|
||||
build-debug: ## Build the reth binary into `target/debug` directory.
|
||||
cargo build --bin reth --features "$(FEATURES)"
|
||||
.PHONY: build-debug-op
|
||||
build-debug-op: ## Build the op-reth binary into `target/debug` directory.
|
||||
cargo build --bin op-reth --features "$(FEATURES)" --manifest-path crates/optimism/bin/Cargo.toml
|
||||
|
||||
.PHONY: build-op
|
||||
build-op: ## Build the op-reth binary into `target` directory.
|
||||
@@ -149,22 +155,6 @@ op-build-x86_64-apple-darwin:
|
||||
op-build-aarch64-apple-darwin:
|
||||
$(MAKE) op-build-native-aarch64-apple-darwin
|
||||
|
||||
build-deb-%:
|
||||
@case "$*" in \
|
||||
x86_64-unknown-linux-gnu|aarch64-unknown-linux-gnu|riscv64gc-unknown-linux-gnu) \
|
||||
echo "Building debian package for $*"; \
|
||||
;; \
|
||||
*) \
|
||||
echo "Error: Debian packages are only supported for x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, and riscv64gc-unknown-linux-gnu, not $*"; \
|
||||
exit 1; \
|
||||
;; \
|
||||
esac
|
||||
cargo install cargo-deb@3.6.0 --locked
|
||||
cargo deb --profile $(PROFILE) --no-build --no-dbgsym --no-strip \
|
||||
--target $* \
|
||||
$(if $(VERSION),--deb-version "1~$(VERSION)") \
|
||||
$(if $(VERSION),--output "target/$*/$(PROFILE)/reth-$(VERSION)-$*-$(PROFILE).deb")
|
||||
|
||||
# Create a `.tar.gz` containing a binary for a specific target.
|
||||
define tarball_release_binary
|
||||
cp $(CARGO_TARGET_DIR)/$(1)/$(PROFILE)/$(2) $(BIN_DIR)/$(2)
|
||||
@@ -390,9 +380,9 @@ db-tools: ## Compile MDBX debugging tools.
|
||||
@echo "Run \"$(DB_TOOLS_DIR)/mdbx_chk\" for the MDBX db file integrity check."
|
||||
|
||||
.PHONY: update-book-cli
|
||||
update-book-cli: build-debug build-debug-op## Update book cli documentation.
|
||||
update-book-cli: build-debug ## Update book cli documentation.
|
||||
@echo "Updating book cli doc..."
|
||||
@./docs/cli/update.sh $(CARGO_TARGET_DIR)/debug/reth $(CARGO_TARGET_DIR)/debug/op-reth
|
||||
@./docs/cli/update.sh $(CARGO_TARGET_DIR)/debug/reth
|
||||
|
||||
.PHONY: profiling
|
||||
profiling: ## Builds `reth` with optimisations, but also symbols.
|
||||
|
||||
@@ -103,8 +103,7 @@ impl BenchmarkRunner {
|
||||
cmd.args(["--wait-time", wait_time]);
|
||||
}
|
||||
|
||||
cmd.env("RUST_LOG_STYLE", "never")
|
||||
.stdout(std::process::Stdio::piped())
|
||||
cmd.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.kill_on_drop(true);
|
||||
|
||||
@@ -191,8 +190,7 @@ impl BenchmarkRunner {
|
||||
cmd.args(["--wait-time", wait_time]);
|
||||
}
|
||||
|
||||
cmd.env("RUST_LOG_STYLE", "never")
|
||||
.stdout(std::process::Stdio::piped())
|
||||
cmd.stdout(std::process::Stdio::piped())
|
||||
.stderr(std::process::Stdio::piped())
|
||||
.kill_on_drop(true);
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ use clap::Parser;
|
||||
use eyre::{eyre, Result, WrapErr};
|
||||
use reth_chainspec::Chain;
|
||||
use reth_cli_runner::CliContext;
|
||||
use reth_node_core::args::{DatadirArgs, LogArgs, TraceArgs};
|
||||
use reth_node_core::args::{DatadirArgs, LogArgs};
|
||||
use reth_tracing::FileWorkerGuard;
|
||||
use std::{net::TcpListener, path::PathBuf, str::FromStr};
|
||||
use tokio::process::Command;
|
||||
@@ -131,19 +131,6 @@ pub(crate) struct Args {
|
||||
#[command(flatten)]
|
||||
pub logs: LogArgs,
|
||||
|
||||
#[command(flatten)]
|
||||
pub traces: TraceArgs,
|
||||
|
||||
/// Maximum queue size for OTLP Batch Span Processor (traces).
|
||||
/// Higher values prevent trace drops when benchmarking many blocks.
|
||||
#[arg(
|
||||
long,
|
||||
value_name = "OTLP_BUFFER_SIZE",
|
||||
default_value = "32768",
|
||||
help_heading = "Tracing"
|
||||
)]
|
||||
pub otlp_max_queue_size: usize,
|
||||
|
||||
/// Additional arguments to pass to baseline reth node command
|
||||
///
|
||||
/// Example: `--baseline-args "--debug.tip 0xabc..."`
|
||||
|
||||
@@ -6,7 +6,6 @@ use csv::Reader;
|
||||
use eyre::{eyre, Result, WrapErr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
cmp::Ordering,
|
||||
collections::HashMap,
|
||||
fs,
|
||||
path::{Path, PathBuf},
|
||||
@@ -37,7 +36,6 @@ pub(crate) struct BenchmarkResults {
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) struct CombinedLatencyRow {
|
||||
pub block_number: u64,
|
||||
pub transaction_count: u64,
|
||||
pub gas_used: u64,
|
||||
pub new_payload_latency: u128,
|
||||
}
|
||||
@@ -46,32 +44,19 @@ pub(crate) struct CombinedLatencyRow {
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub(crate) struct TotalGasRow {
|
||||
pub block_number: u64,
|
||||
pub transaction_count: u64,
|
||||
pub gas_used: u64,
|
||||
pub time: u128,
|
||||
}
|
||||
|
||||
/// Summary statistics for a benchmark run.
|
||||
///
|
||||
/// Latencies are derived from per-block `engine_newPayload` timings (converted from µs to ms):
|
||||
/// - `mean_new_payload_latency_ms`: arithmetic mean latency across blocks.
|
||||
/// - `median_new_payload_latency_ms`: p50 latency across blocks.
|
||||
/// - `p90_new_payload_latency_ms` / `p99_new_payload_latency_ms`: tail latencies across blocks.
|
||||
/// - `std_dev_new_payload_latency_ms`: standard deviation of latency across blocks.
|
||||
/// Summary statistics for a benchmark run
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub(crate) struct BenchmarkSummary {
|
||||
pub total_blocks: u64,
|
||||
pub total_gas_used: u64,
|
||||
pub total_duration_ms: u128,
|
||||
pub mean_new_payload_latency_ms: f64,
|
||||
pub median_new_payload_latency_ms: f64,
|
||||
pub p90_new_payload_latency_ms: f64,
|
||||
pub p99_new_payload_latency_ms: f64,
|
||||
pub std_dev_new_payload_latency_ms: f64,
|
||||
pub avg_new_payload_latency_ms: f64,
|
||||
pub gas_per_second: f64,
|
||||
pub blocks_per_second: f64,
|
||||
pub min_block_number: u64,
|
||||
pub max_block_number: u64,
|
||||
}
|
||||
|
||||
/// Comparison report between two benchmark runs
|
||||
@@ -93,31 +78,10 @@ pub(crate) struct RefInfo {
|
||||
pub end_timestamp: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
/// Summary of the comparison between references.
|
||||
///
|
||||
/// Percent deltas are `(feature - baseline) / baseline * 100`:
|
||||
/// - `new_payload_latency_p50_change_percent` / p90 / p99: percent changes of the respective
|
||||
/// per-block percentiles.
|
||||
/// - `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.
|
||||
/// - `per_block_latency_change_std_dev_percent`: standard deviation of per-block percent changes,
|
||||
/// measuring consistency of performance changes across blocks.
|
||||
/// - `new_payload_total_latency_change_percent` is the percent change of the total newPayload time
|
||||
/// across the run.
|
||||
///
|
||||
/// Positive means slower/higher; negative means faster/lower.
|
||||
/// Summary of the comparison between references
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct ComparisonSummary {
|
||||
pub per_block_latency_change_mean_percent: f64,
|
||||
pub per_block_latency_change_median_percent: f64,
|
||||
pub per_block_latency_change_std_dev_percent: f64,
|
||||
pub new_payload_total_latency_change_percent: f64,
|
||||
pub new_payload_latency_p50_change_percent: f64,
|
||||
pub new_payload_latency_p90_change_percent: f64,
|
||||
pub new_payload_latency_p99_change_percent: f64,
|
||||
pub std_dev_change_percent: f64,
|
||||
pub new_payload_latency_change_percent: f64,
|
||||
pub gas_per_second_change_percent: f64,
|
||||
pub blocks_per_second_change_percent: f64,
|
||||
}
|
||||
@@ -126,8 +90,6 @@ pub(crate) struct ComparisonSummary {
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct BlockComparison {
|
||||
pub block_number: u64,
|
||||
pub transaction_count: u64,
|
||||
pub gas_used: u64,
|
||||
pub baseline_new_payload_latency: u128,
|
||||
pub feature_new_payload_latency: u128,
|
||||
pub new_payload_latency_change_percent: f64,
|
||||
@@ -220,12 +182,10 @@ impl ComparisonGenerator {
|
||||
let feature =
|
||||
self.feature_results.as_ref().ok_or_else(|| eyre!("Feature results not loaded"))?;
|
||||
|
||||
// Generate comparison
|
||||
let comparison_summary =
|
||||
self.calculate_comparison_summary(&baseline.summary, &feature.summary)?;
|
||||
let per_block_comparisons = self.calculate_per_block_comparisons(baseline, feature)?;
|
||||
let comparison_summary = self.calculate_comparison_summary(
|
||||
&baseline.summary,
|
||||
&feature.summary,
|
||||
&per_block_comparisons,
|
||||
)?;
|
||||
|
||||
let report = ComparisonReport {
|
||||
timestamp: self.timestamp.clone(),
|
||||
@@ -315,11 +275,7 @@ impl ComparisonGenerator {
|
||||
Ok(rows)
|
||||
}
|
||||
|
||||
/// Calculate summary statistics for a benchmark run.
|
||||
///
|
||||
/// Computes latency statistics from per-block `new_payload_latency` values in `combined_data`
|
||||
/// (converting from µs to ms), and throughput metrics using the total run duration from
|
||||
/// `total_gas_data`. Percentiles (p50/p90/p99) use linear interpolation on sorted latencies.
|
||||
/// Calculate summary statistics for a benchmark run
|
||||
fn calculate_summary(
|
||||
&self,
|
||||
combined_data: &[CombinedLatencyRow],
|
||||
@@ -334,19 +290,9 @@ impl ComparisonGenerator {
|
||||
|
||||
let total_duration_ms = total_gas_data.last().unwrap().time / 1000; // Convert microseconds to milliseconds
|
||||
|
||||
let latencies_ms: Vec<f64> =
|
||||
combined_data.iter().map(|r| r.new_payload_latency as f64 / 1000.0).collect();
|
||||
let mean_new_payload_latency_ms: f64 =
|
||||
latencies_ms.iter().sum::<f64>() / total_blocks as f64;
|
||||
|
||||
let 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);
|
||||
let p90_new_payload_latency_ms = percentile(&sorted_latencies_ms, 0.9);
|
||||
let p99_new_payload_latency_ms = percentile(&sorted_latencies_ms, 0.99);
|
||||
let avg_new_payload_latency_ms: f64 =
|
||||
combined_data.iter().map(|r| r.new_payload_latency as f64 / 1000.0).sum::<f64>() /
|
||||
total_blocks as f64;
|
||||
|
||||
let total_duration_seconds = total_duration_ms as f64 / 1000.0;
|
||||
let gas_per_second = if total_duration_seconds > f64::EPSILON {
|
||||
@@ -361,22 +307,13 @@ impl ComparisonGenerator {
|
||||
0.0
|
||||
};
|
||||
|
||||
let min_block_number = combined_data.first().unwrap().block_number;
|
||||
let max_block_number = combined_data.last().unwrap().block_number;
|
||||
|
||||
Ok(BenchmarkSummary {
|
||||
total_blocks,
|
||||
total_gas_used,
|
||||
total_duration_ms,
|
||||
mean_new_payload_latency_ms,
|
||||
median_new_payload_latency_ms,
|
||||
p90_new_payload_latency_ms,
|
||||
p99_new_payload_latency_ms,
|
||||
std_dev_new_payload_latency_ms,
|
||||
avg_new_payload_latency_ms,
|
||||
gas_per_second,
|
||||
blocks_per_second,
|
||||
min_block_number,
|
||||
max_block_number,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -385,7 +322,6 @@ impl ComparisonGenerator {
|
||||
&self,
|
||||
baseline: &BenchmarkSummary,
|
||||
feature: &BenchmarkSummary,
|
||||
per_block_comparisons: &[BlockComparison],
|
||||
) -> Result<ComparisonSummary> {
|
||||
let calc_percent_change = |baseline: f64, feature: f64| -> f64 {
|
||||
if baseline.abs() > f64::EPSILON {
|
||||
@@ -395,54 +331,10 @@ impl ComparisonGenerator {
|
||||
}
|
||||
};
|
||||
|
||||
// Calculate per-block statistics. "Per-block" means: for each block, compute the percent
|
||||
// change (feature - baseline) / baseline * 100, then calculate statistics across those
|
||||
// per-block percent changes. This captures how consistently the feature performs relative
|
||||
// to baseline across all blocks.
|
||||
let per_block_percent_changes: Vec<f64> =
|
||||
per_block_comparisons.iter().map(|c| c.new_payload_latency_change_percent).collect();
|
||||
let per_block_latency_change_mean_percent = if per_block_percent_changes.is_empty() {
|
||||
0.0
|
||||
} else {
|
||||
per_block_percent_changes.iter().sum::<f64>() / per_block_percent_changes.len() as f64
|
||||
};
|
||||
let per_block_latency_change_median_percent = if per_block_percent_changes.is_empty() {
|
||||
0.0
|
||||
} else {
|
||||
let mut sorted = per_block_percent_changes.clone();
|
||||
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
|
||||
percentile(&sorted, 0.5)
|
||||
};
|
||||
let per_block_latency_change_std_dev_percent =
|
||||
calculate_std_dev(&per_block_percent_changes, per_block_latency_change_mean_percent);
|
||||
|
||||
let baseline_total_latency_ms =
|
||||
baseline.mean_new_payload_latency_ms * baseline.total_blocks as f64;
|
||||
let feature_total_latency_ms =
|
||||
feature.mean_new_payload_latency_ms * feature.total_blocks as f64;
|
||||
let new_payload_total_latency_change_percent =
|
||||
calc_percent_change(baseline_total_latency_ms, feature_total_latency_ms);
|
||||
|
||||
Ok(ComparisonSummary {
|
||||
per_block_latency_change_mean_percent,
|
||||
per_block_latency_change_median_percent,
|
||||
per_block_latency_change_std_dev_percent,
|
||||
new_payload_total_latency_change_percent,
|
||||
new_payload_latency_p50_change_percent: calc_percent_change(
|
||||
baseline.median_new_payload_latency_ms,
|
||||
feature.median_new_payload_latency_ms,
|
||||
),
|
||||
new_payload_latency_p90_change_percent: calc_percent_change(
|
||||
baseline.p90_new_payload_latency_ms,
|
||||
feature.p90_new_payload_latency_ms,
|
||||
),
|
||||
new_payload_latency_p99_change_percent: calc_percent_change(
|
||||
baseline.p99_new_payload_latency_ms,
|
||||
feature.p99_new_payload_latency_ms,
|
||||
),
|
||||
std_dev_change_percent: calc_percent_change(
|
||||
baseline.std_dev_new_payload_latency_ms,
|
||||
feature.std_dev_new_payload_latency_ms,
|
||||
new_payload_latency_change_percent: calc_percent_change(
|
||||
baseline.avg_new_payload_latency_ms,
|
||||
feature.avg_new_payload_latency_ms,
|
||||
),
|
||||
gas_per_second_change_percent: calc_percent_change(
|
||||
baseline.gas_per_second,
|
||||
@@ -479,8 +371,6 @@ impl ComparisonGenerator {
|
||||
|
||||
let comparison = BlockComparison {
|
||||
block_number: feature_row.block_number,
|
||||
transaction_count: feature_row.transaction_count,
|
||||
gas_used: feature_row.gas_used,
|
||||
baseline_new_payload_latency: baseline_row.new_payload_latency,
|
||||
feature_new_payload_latency: feature_row.new_payload_latency,
|
||||
new_payload_latency_change_percent: calc_percent_change(
|
||||
@@ -546,64 +436,20 @@ impl ComparisonGenerator {
|
||||
let summary = &report.comparison_summary;
|
||||
|
||||
println!("Performance Changes:");
|
||||
println!(
|
||||
" NewPayload Latency per-block mean change: {:+.2}%",
|
||||
summary.per_block_latency_change_mean_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency per-block median change: {:+.2}%",
|
||||
summary.per_block_latency_change_median_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency per-block std dev: {:.2}%",
|
||||
summary.per_block_latency_change_std_dev_percent
|
||||
);
|
||||
println!(
|
||||
" Total newPayload time change: {:+.2}%",
|
||||
summary.new_payload_total_latency_change_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency p50: {:+.2}%",
|
||||
summary.new_payload_latency_p50_change_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency p90: {:+.2}%",
|
||||
summary.new_payload_latency_p90_change_percent
|
||||
);
|
||||
println!(
|
||||
" NewPayload Latency p99: {:+.2}%",
|
||||
summary.new_payload_latency_p99_change_percent
|
||||
);
|
||||
println!(" NewPayload Latency std dev: {:+.2}%", summary.std_dev_change_percent);
|
||||
println!(
|
||||
" Gas/Second: {:+.2}%",
|
||||
summary.gas_per_second_change_percent
|
||||
);
|
||||
println!(
|
||||
" Blocks/Second: {:+.2}%",
|
||||
summary.blocks_per_second_change_percent
|
||||
);
|
||||
println!(" NewPayload Latency: {:+.2}%", summary.new_payload_latency_change_percent);
|
||||
println!(" Gas/Second: {:+.2}%", summary.gas_per_second_change_percent);
|
||||
println!(" Blocks/Second: {:+.2}%", summary.blocks_per_second_change_percent);
|
||||
println!();
|
||||
|
||||
println!("Baseline Summary:");
|
||||
let baseline = &report.baseline.summary;
|
||||
println!(
|
||||
" Blocks: {} (blocks {} to {}), Gas: {}, Duration: {:.2}s",
|
||||
" Blocks: {}, Gas: {}, Duration: {:.2}s",
|
||||
baseline.total_blocks,
|
||||
baseline.min_block_number,
|
||||
baseline.max_block_number,
|
||||
baseline.total_gas_used,
|
||||
baseline.total_duration_ms as f64 / 1000.0
|
||||
);
|
||||
println!(" NewPayload latency (ms):");
|
||||
println!(
|
||||
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}, 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.std_dev_new_payload_latency_ms
|
||||
);
|
||||
println!(" Avg NewPayload: {:.2}ms", baseline.avg_new_payload_latency_ms);
|
||||
if let (Some(start), Some(end)) =
|
||||
(&report.baseline.start_timestamp, &report.baseline.end_timestamp)
|
||||
{
|
||||
@@ -618,22 +464,12 @@ impl ComparisonGenerator {
|
||||
println!("Feature Summary:");
|
||||
let feature = &report.feature.summary;
|
||||
println!(
|
||||
" Blocks: {} (blocks {} to {}), Gas: {}, Duration: {:.2}s",
|
||||
" Blocks: {}, Gas: {}, Duration: {:.2}s",
|
||||
feature.total_blocks,
|
||||
feature.min_block_number,
|
||||
feature.max_block_number,
|
||||
feature.total_gas_used,
|
||||
feature.total_duration_ms as f64 / 1000.0
|
||||
);
|
||||
println!(" NewPayload latency (ms):");
|
||||
println!(
|
||||
" mean: {:.2}, p50: {:.2}, p90: {:.2}, p99: {:.2}, 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.std_dev_new_payload_latency_ms
|
||||
);
|
||||
println!(" Avg NewPayload: {:.2}ms", feature.avg_new_payload_latency_ms);
|
||||
if let (Some(start), Some(end)) =
|
||||
(&report.feature.start_timestamp, &report.feature.end_timestamp)
|
||||
{
|
||||
@@ -646,52 +482,3 @@ impl ComparisonGenerator {
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculate standard deviation from a set of values and their mean.
|
||||
///
|
||||
/// Computes the population standard deviation using the formula:
|
||||
/// `sqrt(sum((x - mean)²) / n)`
|
||||
///
|
||||
/// Returns 0.0 for empty input.
|
||||
fn calculate_std_dev(values: &[f64], mean: f64) -> f64 {
|
||||
if values.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let variance = values
|
||||
.iter()
|
||||
.map(|x| {
|
||||
let diff = x - mean;
|
||||
diff * diff
|
||||
})
|
||||
.sum::<f64>() /
|
||||
values.len() as f64;
|
||||
|
||||
variance.sqrt()
|
||||
}
|
||||
|
||||
/// Calculate percentile using linear interpolation on a sorted slice.
|
||||
///
|
||||
/// Computes `rank = percentile × (n - 1)` where n is the array length. If the rank falls
|
||||
/// between two indices, linearly interpolates between those values. For example, with 100 values,
|
||||
/// p90 computes rank = 0.9 × 99 = 89.1, then returns `values[89] × 0.9 + values[90] × 0.1`.
|
||||
///
|
||||
/// Returns 0.0 for empty input.
|
||||
fn percentile(sorted_values: &[f64], percentile: f64) -> f64 {
|
||||
if sorted_values.is_empty() {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
let clamped = percentile.clamp(0.0, 1.0);
|
||||
let max_index = sorted_values.len() - 1;
|
||||
let rank = clamped * max_index as f64;
|
||||
let lower = rank.floor() as usize;
|
||||
let upper = rank.ceil() as usize;
|
||||
|
||||
if lower == upper {
|
||||
sorted_values[lower]
|
||||
} else {
|
||||
let weight = rank - lower as f64;
|
||||
sorted_values[lower].mul_add(1.0 - weight, sorted_values[upper] * weight)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,43 +181,45 @@ impl GitManager {
|
||||
/// Validate that the specified git references exist (branches, tags, or commits)
|
||||
pub(crate) fn validate_refs(&self, refs: &[&str]) -> Result<()> {
|
||||
for &git_ref in refs {
|
||||
// Try to resolve the ref similar to `git checkout` by peeling to a commit.
|
||||
// First try the ref as-is with ^{commit}, then fall back to origin/{ref}^{commit}.
|
||||
let as_is = format!("{git_ref}^{{commit}}");
|
||||
let ref_check = Command::new("git")
|
||||
.args(["rev-parse", "--verify", &as_is])
|
||||
// Try branch first, then tag, then commit
|
||||
let branch_check = Command::new("git")
|
||||
.args(["rev-parse", "--verify", &format!("refs/heads/{git_ref}")])
|
||||
.current_dir(&self.repo_root)
|
||||
.output();
|
||||
|
||||
let found = if let Ok(output) = ref_check &&
|
||||
let tag_check = Command::new("git")
|
||||
.args(["rev-parse", "--verify", &format!("refs/tags/{git_ref}")])
|
||||
.current_dir(&self.repo_root)
|
||||
.output();
|
||||
|
||||
let commit_check = Command::new("git")
|
||||
.args(["rev-parse", "--verify", &format!("{git_ref}^{{commit}}")])
|
||||
.current_dir(&self.repo_root)
|
||||
.output();
|
||||
|
||||
let found = if let Ok(output) = branch_check &&
|
||||
output.status.success()
|
||||
{
|
||||
info!("Validated reference exists: {}", git_ref);
|
||||
info!("Validated branch exists: {}", git_ref);
|
||||
true
|
||||
} else if let Ok(output) = tag_check &&
|
||||
output.status.success()
|
||||
{
|
||||
info!("Validated tag exists: {}", git_ref);
|
||||
true
|
||||
} else if let Ok(output) = commit_check &&
|
||||
output.status.success()
|
||||
{
|
||||
info!("Validated commit exists: {}", git_ref);
|
||||
true
|
||||
} else {
|
||||
// Try remote-only branches via origin/{ref}
|
||||
let origin_ref = format!("origin/{git_ref}^{{commit}}");
|
||||
let origin_check = Command::new("git")
|
||||
.args(["rev-parse", "--verify", &origin_ref])
|
||||
.current_dir(&self.repo_root)
|
||||
.output();
|
||||
|
||||
if let Ok(output) = origin_check &&
|
||||
output.status.success()
|
||||
{
|
||||
info!("Validated remote reference exists: origin/{}", git_ref);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
false
|
||||
};
|
||||
|
||||
if !found {
|
||||
return Err(eyre!(
|
||||
"Git reference '{}' does not exist as branch, tag, or commit (tried '{}' and 'origin/{}^{{commit}}')",
|
||||
git_ref,
|
||||
format!("{git_ref}^{{commit}}"),
|
||||
git_ref,
|
||||
"Git reference '{}' does not exist as branch, tag, or commit",
|
||||
git_ref
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,8 +29,6 @@ pub(crate) struct NodeManager {
|
||||
output_dir: PathBuf,
|
||||
additional_reth_args: Vec<String>,
|
||||
comparison_dir: Option<PathBuf>,
|
||||
tracing_endpoint: Option<String>,
|
||||
otlp_max_queue_size: usize,
|
||||
}
|
||||
|
||||
impl NodeManager {
|
||||
@@ -44,16 +42,8 @@ impl NodeManager {
|
||||
binary_path: None,
|
||||
enable_profiling: args.profile,
|
||||
output_dir: args.output_dir_path(),
|
||||
// Filter out empty strings to prevent invalid arguments being passed to reth node
|
||||
additional_reth_args: args
|
||||
.reth_args
|
||||
.iter()
|
||||
.filter(|s| !s.is_empty())
|
||||
.cloned()
|
||||
.collect(),
|
||||
additional_reth_args: args.reth_args.clone(),
|
||||
comparison_dir: None,
|
||||
tracing_endpoint: args.traces.otlp.as_ref().map(|u| u.to_string()),
|
||||
otlp_max_queue_size: args.otlp_max_queue_size,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,7 +119,6 @@ impl NodeManager {
|
||||
&self,
|
||||
binary_path_str: &str,
|
||||
additional_args: &[String],
|
||||
ref_type: &str,
|
||||
) -> (Vec<String>, String) {
|
||||
let mut reth_args = vec![binary_path_str.to_string(), "node".to_string()];
|
||||
|
||||
@@ -157,13 +146,6 @@ impl NodeManager {
|
||||
"--trusted-only".to_string(),
|
||||
]);
|
||||
|
||||
// Add tracing arguments if OTLP endpoint is configured
|
||||
if let Some(ref endpoint) = self.tracing_endpoint {
|
||||
info!("Enabling OTLP tracing export to: {} (service: reth-{})", endpoint, ref_type);
|
||||
// Endpoint requires equals per clap settings in reth
|
||||
reth_args.push(format!("--tracing-otlp={}", endpoint));
|
||||
}
|
||||
|
||||
// Add any additional arguments passed via command line (common to both baseline and
|
||||
// feature)
|
||||
reth_args.extend_from_slice(&self.additional_reth_args);
|
||||
@@ -211,9 +193,6 @@ impl NodeManager {
|
||||
cmd.arg("--");
|
||||
cmd.args(reth_args);
|
||||
|
||||
// Set environment variable to disable log styling
|
||||
cmd.env("RUST_LOG_STYLE", "never");
|
||||
|
||||
Ok(cmd)
|
||||
}
|
||||
|
||||
@@ -221,22 +200,17 @@ impl NodeManager {
|
||||
fn create_direct_command(&self, reth_args: &[String]) -> Command {
|
||||
let binary_path = &reth_args[0];
|
||||
|
||||
let mut cmd = if self.use_sudo {
|
||||
if self.use_sudo {
|
||||
info!("Starting reth node with sudo...");
|
||||
let mut sudo_cmd = Command::new("sudo");
|
||||
sudo_cmd.args(reth_args);
|
||||
sudo_cmd
|
||||
let mut cmd = Command::new("sudo");
|
||||
cmd.args(reth_args);
|
||||
cmd
|
||||
} else {
|
||||
info!("Starting reth node...");
|
||||
let mut reth_cmd = Command::new(binary_path);
|
||||
reth_cmd.args(&reth_args[1..]); // Skip the binary path since it's the command
|
||||
reth_cmd
|
||||
};
|
||||
|
||||
// Set environment variable to disable log styling
|
||||
cmd.env("RUST_LOG_STYLE", "never");
|
||||
|
||||
cmd
|
||||
let mut cmd = Command::new(binary_path);
|
||||
cmd.args(&reth_args[1..]); // Skip the binary path since it's the command
|
||||
cmd
|
||||
}
|
||||
}
|
||||
|
||||
/// Start a reth node using the specified binary path and return the process handle
|
||||
@@ -251,7 +225,7 @@ impl NodeManager {
|
||||
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);
|
||||
let (reth_args, _) = self.build_reth_args(&binary_path_str, additional_args);
|
||||
|
||||
// Log additional arguments if any
|
||||
if !self.additional_reth_args.is_empty() {
|
||||
@@ -273,15 +247,6 @@ impl NodeManager {
|
||||
cmd.process_group(0);
|
||||
}
|
||||
|
||||
// Set high queue size to prevent trace dropping during benchmarks
|
||||
if self.tracing_endpoint.is_some() {
|
||||
cmd.env("OTEL_BSP_MAX_QUEUE_SIZE", self.otlp_max_queue_size.to_string()); // Traces
|
||||
cmd.env("OTEL_BLRP_MAX_QUEUE_SIZE", "10000"); // Logs
|
||||
|
||||
// Set service name to differentiate baseline vs feature runs in Jaeger
|
||||
cmd.env("OTEL_SERVICE_NAME", format!("reth-{}", ref_type));
|
||||
}
|
||||
|
||||
debug!("Executing reth command: {cmd:?}");
|
||||
|
||||
let mut child = cmd
|
||||
@@ -503,9 +468,6 @@ impl NodeManager {
|
||||
|
||||
cmd.args(["to-block", &block_number.to_string()]);
|
||||
|
||||
// Set environment variable to disable log styling
|
||||
cmd.env("RUST_LOG_STYLE", "never");
|
||||
|
||||
// Debug log the command
|
||||
debug!("Executing reth unwind command: {:?}", cmd);
|
||||
|
||||
|
||||
@@ -79,13 +79,22 @@ impl Command {
|
||||
break;
|
||||
}
|
||||
};
|
||||
let header = block.header.clone();
|
||||
|
||||
let head_block_hash = block.header.hash;
|
||||
let safe_block_hash = block_provider
|
||||
.get_block_by_number(block.header.number.saturating_sub(32).into());
|
||||
let (version, params) = match block_to_new_payload(block, is_optimism) {
|
||||
Ok(result) => result,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to convert block to new payload: {e}");
|
||||
let _ = error_sender.send(e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
let head_block_hash = header.hash;
|
||||
let safe_block_hash =
|
||||
block_provider.get_block_by_number(header.number.saturating_sub(32).into());
|
||||
|
||||
let finalized_block_hash = block_provider
|
||||
.get_block_by_number(block.header.number.saturating_sub(64).into());
|
||||
let finalized_block_hash =
|
||||
block_provider.get_block_by_number(header.number.saturating_sub(64).into());
|
||||
|
||||
let (safe, finalized) = tokio::join!(safe_block_hash, finalized_block_hash,);
|
||||
|
||||
@@ -101,7 +110,14 @@ impl Command {
|
||||
|
||||
next_block += 1;
|
||||
if let Err(e) = sender
|
||||
.send((block, head_block_hash, safe_block_hash, finalized_block_hash))
|
||||
.send((
|
||||
header,
|
||||
version,
|
||||
params,
|
||||
head_block_hash,
|
||||
safe_block_hash,
|
||||
finalized_block_hash,
|
||||
))
|
||||
.await
|
||||
{
|
||||
tracing::error!("Failed to send block data: {e}");
|
||||
@@ -115,16 +131,15 @@ impl Command {
|
||||
let total_benchmark_duration = Instant::now();
|
||||
let mut total_wait_time = Duration::ZERO;
|
||||
|
||||
while let Some((block, head, safe, finalized)) = {
|
||||
while let Some((header, version, params, head, safe, finalized)) = {
|
||||
let wait_start = Instant::now();
|
||||
let result = receiver.recv().await;
|
||||
total_wait_time += wait_start.elapsed();
|
||||
result
|
||||
} {
|
||||
// just put gas used here
|
||||
let gas_used = block.header.gas_used;
|
||||
let block_number = block.header.number;
|
||||
let transaction_count = block.transactions.len() as u64;
|
||||
let gas_used = header.gas_used;
|
||||
let block_number = header.number;
|
||||
|
||||
debug!(target: "reth-bench", ?block_number, "Sending payload",);
|
||||
|
||||
@@ -135,7 +150,6 @@ impl Command {
|
||||
finalized_block_hash: finalized,
|
||||
};
|
||||
|
||||
let (version, params) = block_to_new_payload(block, is_optimism)?;
|
||||
let start = Instant::now();
|
||||
call_new_payload(&auth_provider, version, params).await?;
|
||||
|
||||
@@ -146,13 +160,8 @@ impl Command {
|
||||
// calculate the total duration and the fcu latency, record
|
||||
let total_latency = start.elapsed();
|
||||
let fcu_latency = total_latency - new_payload_result.latency;
|
||||
let combined_result = CombinedResult {
|
||||
block_number,
|
||||
transaction_count,
|
||||
new_payload_result,
|
||||
fcu_latency,
|
||||
total_latency,
|
||||
};
|
||||
let combined_result =
|
||||
CombinedResult { block_number, new_payload_result, fcu_latency, total_latency };
|
||||
|
||||
// current duration since the start of the benchmark minus the time
|
||||
// waiting for blocks
|
||||
@@ -165,8 +174,7 @@ impl Command {
|
||||
tokio::time::sleep(self.wait_time).await;
|
||||
|
||||
// record the current result
|
||||
let gas_row =
|
||||
TotalGasRow { block_number, transaction_count, gas_used, time: current_duration };
|
||||
let gas_row = TotalGasRow { block_number, gas_used, time: current_duration };
|
||||
results.push((gas_row, combined_result));
|
||||
}
|
||||
|
||||
|
||||
@@ -72,9 +72,19 @@ impl Command {
|
||||
break;
|
||||
}
|
||||
};
|
||||
let header = block.header.clone();
|
||||
|
||||
let (version, params) = match block_to_new_payload(block, is_optimism) {
|
||||
Ok(result) => result,
|
||||
Err(e) => {
|
||||
tracing::error!("Failed to convert block to new payload: {e}");
|
||||
let _ = error_sender.send(e);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
next_block += 1;
|
||||
if let Err(e) = sender.send(block).await {
|
||||
if let Err(e) = sender.send((header, version, params)).await {
|
||||
tracing::error!("Failed to send block data: {e}");
|
||||
break;
|
||||
}
|
||||
@@ -86,24 +96,23 @@ impl Command {
|
||||
let total_benchmark_duration = Instant::now();
|
||||
let mut total_wait_time = Duration::ZERO;
|
||||
|
||||
while let Some(block) = {
|
||||
while let Some((header, version, params)) = {
|
||||
let wait_start = Instant::now();
|
||||
let result = receiver.recv().await;
|
||||
total_wait_time += wait_start.elapsed();
|
||||
result
|
||||
} {
|
||||
let block_number = block.header.number;
|
||||
let transaction_count = block.transactions.len() as u64;
|
||||
let gas_used = block.header.gas_used;
|
||||
// just put gas used here
|
||||
let gas_used = header.gas_used;
|
||||
|
||||
let block_number = header.number;
|
||||
|
||||
debug!(
|
||||
target: "reth-bench",
|
||||
number=?block.header.number,
|
||||
number=?header.number,
|
||||
"Sending payload to engine",
|
||||
);
|
||||
|
||||
let (version, params) = block_to_new_payload(block, is_optimism)?;
|
||||
|
||||
let start = Instant::now();
|
||||
call_new_payload(&auth_provider, version, params).await?;
|
||||
|
||||
@@ -115,8 +124,7 @@ impl Command {
|
||||
let current_duration = total_benchmark_duration.elapsed() - total_wait_time;
|
||||
|
||||
// record the current result
|
||||
let row =
|
||||
TotalGasRow { block_number, transaction_count, gas_used, time: current_duration };
|
||||
let row = TotalGasRow { block_number, gas_used, time: current_duration };
|
||||
results.push((row, new_payload_result));
|
||||
}
|
||||
|
||||
|
||||
@@ -67,8 +67,6 @@ impl Serialize for NewPayloadResult {
|
||||
pub(crate) struct CombinedResult {
|
||||
/// The block number of the block being processed.
|
||||
pub(crate) block_number: u64,
|
||||
/// The number of transactions in the block.
|
||||
pub(crate) transaction_count: u64,
|
||||
/// The `newPayload` result.
|
||||
pub(crate) new_payload_result: NewPayloadResult,
|
||||
/// The latency of the `forkchoiceUpdated` call.
|
||||
@@ -110,11 +108,10 @@ impl Serialize for CombinedResult {
|
||||
let fcu_latency = self.fcu_latency.as_micros();
|
||||
let new_payload_latency = self.new_payload_result.latency.as_micros();
|
||||
let total_latency = self.total_latency.as_micros();
|
||||
let mut state = serializer.serialize_struct("CombinedResult", 6)?;
|
||||
let mut state = serializer.serialize_struct("CombinedResult", 5)?;
|
||||
|
||||
// flatten the new payload result because this is meant for CSV writing
|
||||
state.serialize_field("block_number", &self.block_number)?;
|
||||
state.serialize_field("transaction_count", &self.transaction_count)?;
|
||||
state.serialize_field("gas_used", &self.new_payload_result.gas_used)?;
|
||||
state.serialize_field("new_payload_latency", &new_payload_latency)?;
|
||||
state.serialize_field("fcu_latency", &fcu_latency)?;
|
||||
@@ -128,8 +125,6 @@ impl Serialize for CombinedResult {
|
||||
pub(crate) struct TotalGasRow {
|
||||
/// The block number of the block being processed.
|
||||
pub(crate) block_number: u64,
|
||||
/// The number of transactions in the block.
|
||||
pub(crate) transaction_count: u64,
|
||||
/// The total gas used in the block.
|
||||
pub(crate) gas_used: u64,
|
||||
/// Time since the start of the benchmark.
|
||||
@@ -177,9 +172,8 @@ impl Serialize for TotalGasRow {
|
||||
{
|
||||
// convert the time to microseconds
|
||||
let time = self.time.as_micros();
|
||||
let mut state = serializer.serialize_struct("TotalGasRow", 4)?;
|
||||
let mut state = serializer.serialize_struct("TotalGasRow", 3)?;
|
||||
state.serialize_field("block_number", &self.block_number)?;
|
||||
state.serialize_field("transaction_count", &self.transaction_count)?;
|
||||
state.serialize_field("gas_used", &self.gas_used)?;
|
||||
state.serialize_field("time", &time)?;
|
||||
state.end()
|
||||
@@ -194,12 +188,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_write_total_gas_row_csv() {
|
||||
let row = TotalGasRow {
|
||||
block_number: 1,
|
||||
transaction_count: 10,
|
||||
gas_used: 1_000,
|
||||
time: Duration::from_secs(1),
|
||||
};
|
||||
let row = TotalGasRow { block_number: 1, gas_used: 1_000, time: Duration::from_secs(1) };
|
||||
|
||||
let mut writer = Writer::from_writer(vec![]);
|
||||
writer.serialize(row).unwrap();
|
||||
@@ -209,11 +198,11 @@ mod tests {
|
||||
let mut result = result.as_slice().lines();
|
||||
|
||||
// assert header
|
||||
let expected_first_line = "block_number,transaction_count,gas_used,time";
|
||||
let expected_first_line = "block_number,gas_used,time";
|
||||
let first_line = result.next().unwrap().unwrap();
|
||||
assert_eq!(first_line, expected_first_line);
|
||||
|
||||
let expected_second_line = "1,10,1000,1000000";
|
||||
let expected_second_line = "1,1000,1000000";
|
||||
let second_line = result.next().unwrap().unwrap();
|
||||
assert_eq!(second_line, expected_second_line);
|
||||
}
|
||||
|
||||
@@ -9,20 +9,6 @@ repository.workspace = true
|
||||
description = "Reth node implementation"
|
||||
default-run = "reth"
|
||||
|
||||
[package.metadata.deb]
|
||||
maintainer = "reth team"
|
||||
depends = "$auto"
|
||||
section = "network"
|
||||
priority = "optional"
|
||||
maintainer-scripts = "../../pkg/reth/debian/"
|
||||
assets = [
|
||||
"$auto",
|
||||
["../../README.md", "usr/share/doc/reth/", "644"],
|
||||
["../../LICENSE-APACHE", "usr/share/doc/reth/", "644"],
|
||||
["../../LICENSE-MIT", "usr/share/doc/reth/", "644"],
|
||||
]
|
||||
systemd-units = { enable = false, start = false, unit-name = "reth", unit-scripts = "../../pkg/reth/debian" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
@@ -114,12 +100,6 @@ jemalloc-prof = [
|
||||
"reth-cli-util/jemalloc-prof",
|
||||
"reth-ethereum-cli/jemalloc-prof",
|
||||
]
|
||||
jemalloc-unprefixed = [
|
||||
"reth-cli-util/jemalloc-unprefixed",
|
||||
"reth-node-core/jemalloc",
|
||||
"reth-node-metrics/jemalloc",
|
||||
"reth-ethereum-cli/jemalloc",
|
||||
]
|
||||
tracy-allocator = [
|
||||
"reth-cli-util/tracy-allocator",
|
||||
"reth-ethereum-cli/tracy-allocator",
|
||||
|
||||
@@ -1,444 +0,0 @@
|
||||
use alloy_primitives::B256;
|
||||
use parking_lot::Mutex;
|
||||
use reth_metrics::{metrics::Counter, Metrics};
|
||||
use reth_trie::{
|
||||
updates::{TrieUpdates, TrieUpdatesSorted},
|
||||
HashedPostState, HashedPostStateSorted, TrieInputSorted,
|
||||
};
|
||||
use std::{
|
||||
fmt,
|
||||
sync::{Arc, LazyLock},
|
||||
};
|
||||
use tracing::instrument;
|
||||
|
||||
/// Shared handle to asynchronously populated trie data.
|
||||
///
|
||||
/// Uses a try-lock + fallback computation approach for deadlock-free access.
|
||||
/// If the deferred task hasn't completed, computes trie data synchronously
|
||||
/// from stored unsorted inputs rather than blocking.
|
||||
#[derive(Clone)]
|
||||
pub struct DeferredTrieData {
|
||||
/// Shared deferred state holding either raw inputs (pending) or computed result (ready).
|
||||
state: Arc<Mutex<DeferredState>>,
|
||||
}
|
||||
|
||||
/// Sorted trie data computed for an executed block.
|
||||
/// These represent the complete set of sorted trie data required to persist
|
||||
/// block state for, and generate proofs on top of, a block.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct ComputedTrieData {
|
||||
/// Sorted hashed post-state produced by execution.
|
||||
pub hashed_state: Arc<HashedPostStateSorted>,
|
||||
/// Sorted trie updates produced by state root computation.
|
||||
pub trie_updates: Arc<TrieUpdatesSorted>,
|
||||
/// Trie input bundled with its anchor hash, if available.
|
||||
pub anchored_trie_input: Option<AnchoredTrieInput>,
|
||||
}
|
||||
|
||||
/// Trie input bundled with its anchor hash.
|
||||
///
|
||||
/// This is used to store the trie input and anchor hash for a block together.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct AnchoredTrieInput {
|
||||
/// The persisted ancestor hash this trie input is anchored to.
|
||||
pub anchor_hash: B256,
|
||||
/// Trie input constructed from in-memory overlays.
|
||||
pub trie_input: Arc<TrieInputSorted>,
|
||||
}
|
||||
|
||||
/// Metrics for deferred trie computation.
|
||||
#[derive(Metrics)]
|
||||
#[metrics(scope = "sync.block_validation")]
|
||||
struct DeferredTrieMetrics {
|
||||
/// Number of times deferred trie data was ready (async task completed first).
|
||||
deferred_trie_async_ready: Counter,
|
||||
/// Number of times deferred trie data required synchronous computation (fallback path).
|
||||
deferred_trie_sync_fallback: Counter,
|
||||
}
|
||||
|
||||
static DEFERRED_TRIE_METRICS: LazyLock<DeferredTrieMetrics> =
|
||||
LazyLock::new(DeferredTrieMetrics::default);
|
||||
|
||||
/// Internal state for deferred trie data.
|
||||
enum DeferredState {
|
||||
/// Data is not yet available; raw inputs stored for fallback computation.
|
||||
Pending(PendingInputs),
|
||||
/// Data has been computed and is ready.
|
||||
Ready(ComputedTrieData),
|
||||
}
|
||||
|
||||
/// Inputs kept while a deferred trie computation is pending.
|
||||
#[derive(Clone, Debug)]
|
||||
struct PendingInputs {
|
||||
/// Unsorted hashed post-state from execution.
|
||||
hashed_state: Arc<HashedPostState>,
|
||||
/// Unsorted trie updates from state root computation.
|
||||
trie_updates: Arc<TrieUpdates>,
|
||||
/// The persisted ancestor hash this trie input is anchored to.
|
||||
anchor_hash: B256,
|
||||
/// Deferred trie data from ancestor blocks for merging.
|
||||
ancestors: Vec<DeferredTrieData>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for DeferredTrieData {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let state = self.state.lock();
|
||||
match &*state {
|
||||
DeferredState::Pending(_) => {
|
||||
f.debug_struct("DeferredTrieData").field("state", &"pending").finish()
|
||||
}
|
||||
DeferredState::Ready(_) => {
|
||||
f.debug_struct("DeferredTrieData").field("state", &"ready").finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DeferredTrieData {
|
||||
/// Create a new pending handle with fallback inputs for synchronous computation.
|
||||
///
|
||||
/// If the async task hasn't completed when `wait_cloned` is called, the trie data
|
||||
/// will be computed synchronously from these inputs. This eliminates deadlock risk.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `hashed_state` - Unsorted hashed post-state from execution
|
||||
/// * `trie_updates` - Unsorted trie updates from state root computation
|
||||
/// * `anchor_hash` - The persisted ancestor hash this trie input is anchored to
|
||||
/// * `ancestors` - Deferred trie data from ancestor blocks for merging
|
||||
pub fn pending(
|
||||
hashed_state: Arc<HashedPostState>,
|
||||
trie_updates: Arc<TrieUpdates>,
|
||||
anchor_hash: B256,
|
||||
ancestors: Vec<Self>,
|
||||
) -> Self {
|
||||
Self {
|
||||
state: Arc::new(Mutex::new(DeferredState::Pending(PendingInputs {
|
||||
hashed_state,
|
||||
trie_updates,
|
||||
anchor_hash,
|
||||
ancestors,
|
||||
}))),
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a handle that is already populated with the given [`ComputedTrieData`].
|
||||
///
|
||||
/// Useful when trie data is available immediately.
|
||||
/// [`Self::wait_cloned`] will return without any computation.
|
||||
pub fn ready(bundle: ComputedTrieData) -> Self {
|
||||
Self { state: Arc::new(Mutex::new(DeferredState::Ready(bundle))) }
|
||||
}
|
||||
|
||||
/// Sort block execution outputs and build a [`TrieInputSorted`] overlay.
|
||||
///
|
||||
/// The trie input overlay accumulates sorted hashed state (account/storage changes) and
|
||||
/// trie node updates from all in-memory ancestor blocks. This overlay is required for:
|
||||
/// - Computing state roots on top of in-memory blocks
|
||||
/// - Generating storage/account proofs for unpersisted state
|
||||
///
|
||||
/// # Process
|
||||
/// 1. Sort the current block's hashed state and trie updates
|
||||
/// 2. Merge ancestor overlays (oldest -> newest, so later state takes precedence)
|
||||
/// 3. Extend the merged overlay with this block's sorted data
|
||||
///
|
||||
/// Used by both the async background task and the synchronous fallback path.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `hashed_state` - Unsorted hashed post-state (account/storage changes) from execution
|
||||
/// * `trie_updates` - Unsorted trie node updates from state root computation
|
||||
/// * `anchor_hash` - The persisted ancestor hash this trie input is anchored to
|
||||
/// * `ancestors` - Deferred trie data from ancestor blocks for merging
|
||||
pub fn sort_and_build_trie_input(
|
||||
hashed_state: &HashedPostState,
|
||||
trie_updates: &TrieUpdates,
|
||||
anchor_hash: B256,
|
||||
ancestors: &[Self],
|
||||
) -> ComputedTrieData {
|
||||
// Sort the current block's hashed state and trie updates
|
||||
let sorted_hashed_state = Arc::new(hashed_state.clone_into_sorted());
|
||||
let sorted_trie_updates = Arc::new(trie_updates.clone().into_sorted());
|
||||
|
||||
// Merge trie data from ancestors (oldest -> newest so later state takes precedence)
|
||||
let mut overlay = TrieInputSorted::default();
|
||||
for ancestor in ancestors {
|
||||
let ancestor_data = ancestor.wait_cloned();
|
||||
{
|
||||
let state_mut = Arc::make_mut(&mut overlay.state);
|
||||
state_mut.extend_ref(ancestor_data.hashed_state.as_ref());
|
||||
}
|
||||
{
|
||||
let nodes_mut = Arc::make_mut(&mut overlay.nodes);
|
||||
nodes_mut.extend_ref(ancestor_data.trie_updates.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
// Extend overlay with current block's sorted data
|
||||
{
|
||||
let state_mut = Arc::make_mut(&mut overlay.state);
|
||||
state_mut.extend_ref(sorted_hashed_state.as_ref());
|
||||
}
|
||||
{
|
||||
let nodes_mut = Arc::make_mut(&mut overlay.nodes);
|
||||
nodes_mut.extend_ref(sorted_trie_updates.as_ref());
|
||||
}
|
||||
|
||||
ComputedTrieData::with_trie_input(
|
||||
sorted_hashed_state,
|
||||
sorted_trie_updates,
|
||||
anchor_hash,
|
||||
Arc::new(overlay),
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns trie data, computing synchronously if the async task hasn't completed.
|
||||
///
|
||||
/// - If the async task has completed (`Ready`), returns the cached result.
|
||||
/// - If pending, computes synchronously from stored inputs.
|
||||
///
|
||||
/// Deadlock is avoided as long as the provided ancestors form a true ancestor chain (a DAG):
|
||||
/// - Each block only waits on its ancestors (blocks on the path to the persisted root)
|
||||
/// - Sibling blocks (forks) are never in each other's ancestor lists
|
||||
/// - A block never waits on its descendants
|
||||
///
|
||||
/// Given that invariant, circular wait dependencies are impossible.
|
||||
#[instrument(level = "debug", target = "engine::tree::deferred_trie", skip_all)]
|
||||
pub fn wait_cloned(&self) -> ComputedTrieData {
|
||||
let mut state = self.state.lock();
|
||||
match &*state {
|
||||
// If the deferred trie data is ready, return the cached result.
|
||||
DeferredState::Ready(bundle) => {
|
||||
DEFERRED_TRIE_METRICS.deferred_trie_async_ready.increment(1);
|
||||
bundle.clone()
|
||||
}
|
||||
// If the deferred trie data is pending, compute the trie data synchronously and return
|
||||
// the result. This is the fallback path if the async task hasn't completed.
|
||||
DeferredState::Pending(inputs) => {
|
||||
DEFERRED_TRIE_METRICS.deferred_trie_sync_fallback.increment(1);
|
||||
let computed = Self::sort_and_build_trie_input(
|
||||
&inputs.hashed_state,
|
||||
&inputs.trie_updates,
|
||||
inputs.anchor_hash,
|
||||
&inputs.ancestors,
|
||||
);
|
||||
*state = DeferredState::Ready(computed.clone());
|
||||
computed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedTrieData {
|
||||
/// Construct a bundle that includes trie input anchored to a persisted ancestor.
|
||||
pub const fn with_trie_input(
|
||||
hashed_state: Arc<HashedPostStateSorted>,
|
||||
trie_updates: Arc<TrieUpdatesSorted>,
|
||||
anchor_hash: B256,
|
||||
trie_input: Arc<TrieInputSorted>,
|
||||
) -> Self {
|
||||
Self {
|
||||
hashed_state,
|
||||
trie_updates,
|
||||
anchored_trie_input: Some(AnchoredTrieInput { anchor_hash, trie_input }),
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct a bundle without trie input or anchor information.
|
||||
///
|
||||
/// Unlike [`Self::with_trie_input`], this constructor omits the accumulated trie input overlay
|
||||
/// and its anchor hash. Use this when the trie input is not needed, such as in block builders
|
||||
/// or sequencers that don't require proof generation on top of in-memory state.
|
||||
///
|
||||
/// The trie input anchor identifies the persisted block hash from which the in-memory overlay
|
||||
/// was built. Without it, consumers cannot determine which on-disk state to combine with.
|
||||
pub const fn without_trie_input(
|
||||
hashed_state: Arc<HashedPostStateSorted>,
|
||||
trie_updates: Arc<TrieUpdatesSorted>,
|
||||
) -> Self {
|
||||
Self { hashed_state, trie_updates, anchored_trie_input: None }
|
||||
}
|
||||
|
||||
/// Returns the anchor hash, if present.
|
||||
pub fn anchor_hash(&self) -> Option<B256> {
|
||||
self.anchored_trie_input.as_ref().map(|anchored| anchored.anchor_hash)
|
||||
}
|
||||
|
||||
/// Returns the trie input, if present.
|
||||
pub fn trie_input(&self) -> Option<&Arc<TrieInputSorted>> {
|
||||
self.anchored_trie_input.as_ref().map(|anchored| &anchored.trie_input)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use alloy_primitives::{map::B256Map, U256};
|
||||
use reth_primitives_traits::Account;
|
||||
use reth_trie::updates::TrieUpdates;
|
||||
use std::{
|
||||
sync::Arc,
|
||||
thread,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
fn empty_bundle() -> ComputedTrieData {
|
||||
ComputedTrieData {
|
||||
hashed_state: Arc::default(),
|
||||
trie_updates: Arc::default(),
|
||||
anchored_trie_input: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn empty_pending() -> DeferredTrieData {
|
||||
empty_pending_with_anchor(B256::ZERO)
|
||||
}
|
||||
|
||||
fn empty_pending_with_anchor(anchor: B256) -> DeferredTrieData {
|
||||
DeferredTrieData::pending(
|
||||
Arc::new(HashedPostState::default()),
|
||||
Arc::new(TrieUpdates::default()),
|
||||
anchor,
|
||||
Vec::new(),
|
||||
)
|
||||
}
|
||||
|
||||
/// Verifies that a ready handle returns immediately without computation.
|
||||
#[test]
|
||||
fn ready_returns_immediately() {
|
||||
let bundle = empty_bundle();
|
||||
let deferred = DeferredTrieData::ready(bundle.clone());
|
||||
|
||||
let start = Instant::now();
|
||||
let result = deferred.wait_cloned();
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
assert_eq!(result.hashed_state, bundle.hashed_state);
|
||||
assert_eq!(result.trie_updates, bundle.trie_updates);
|
||||
assert_eq!(result.anchor_hash(), bundle.anchor_hash());
|
||||
assert!(elapsed < Duration::from_millis(20));
|
||||
}
|
||||
|
||||
/// Verifies that a pending handle computes trie data synchronously via fallback.
|
||||
#[test]
|
||||
fn pending_computes_fallback() {
|
||||
let deferred = empty_pending();
|
||||
|
||||
// wait_cloned should compute from inputs without blocking
|
||||
let start = Instant::now();
|
||||
let result = deferred.wait_cloned();
|
||||
let elapsed = start.elapsed();
|
||||
|
||||
// Should return quickly (fallback computation)
|
||||
assert!(elapsed < Duration::from_millis(100));
|
||||
assert!(result.hashed_state.is_empty());
|
||||
}
|
||||
|
||||
/// Verifies that fallback computation result is cached for subsequent calls.
|
||||
#[test]
|
||||
fn fallback_result_is_cached() {
|
||||
let deferred = empty_pending();
|
||||
|
||||
// First call computes and should stash the result
|
||||
let first = deferred.wait_cloned();
|
||||
// Second call should reuse the cached result (same Arc pointer)
|
||||
let second = deferred.wait_cloned();
|
||||
|
||||
assert!(Arc::ptr_eq(&first.hashed_state, &second.hashed_state));
|
||||
assert!(Arc::ptr_eq(&first.trie_updates, &second.trie_updates));
|
||||
assert_eq!(first.anchor_hash(), second.anchor_hash());
|
||||
}
|
||||
|
||||
/// Verifies that concurrent `wait_cloned` calls result in only one computation,
|
||||
/// with all callers receiving the same cached result.
|
||||
#[test]
|
||||
fn concurrent_wait_cloned_computes_once() {
|
||||
let deferred = empty_pending();
|
||||
|
||||
// Spawn multiple threads that all call wait_cloned concurrently
|
||||
let handles: Vec<_> = (0..10)
|
||||
.map(|_| {
|
||||
let d = deferred.clone();
|
||||
thread::spawn(move || d.wait_cloned())
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Collect all results
|
||||
let results: Vec<_> = handles.into_iter().map(|h| h.join().unwrap()).collect();
|
||||
|
||||
// All results should share the same Arc pointers (same computed result)
|
||||
let first = &results[0];
|
||||
for result in &results[1..] {
|
||||
assert!(Arc::ptr_eq(&first.hashed_state, &result.hashed_state));
|
||||
assert!(Arc::ptr_eq(&first.trie_updates, &result.trie_updates));
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests that ancestor trie data is merged during fallback computation and that the
|
||||
/// resulting `ComputedTrieData` uses the current block's anchor hash, not the ancestor's.
|
||||
#[test]
|
||||
fn ancestors_are_merged() {
|
||||
// Create ancestor with some data
|
||||
let ancestor_bundle = ComputedTrieData {
|
||||
hashed_state: Arc::default(),
|
||||
trie_updates: Arc::default(),
|
||||
anchored_trie_input: Some(AnchoredTrieInput {
|
||||
anchor_hash: B256::with_last_byte(1),
|
||||
trie_input: Arc::new(TrieInputSorted::default()),
|
||||
}),
|
||||
};
|
||||
let ancestor = DeferredTrieData::ready(ancestor_bundle);
|
||||
|
||||
// Create pending with ancestor
|
||||
let deferred = DeferredTrieData::pending(
|
||||
Arc::new(HashedPostState::default()),
|
||||
Arc::new(TrieUpdates::default()),
|
||||
B256::with_last_byte(2),
|
||||
vec![ancestor],
|
||||
);
|
||||
|
||||
let result = deferred.wait_cloned();
|
||||
// Should have the current block's anchor, not the ancestor's
|
||||
assert_eq!(result.anchor_hash(), Some(B256::with_last_byte(2)));
|
||||
}
|
||||
|
||||
/// Ensures ancestor overlays are merged oldest -> newest so latest state wins (no overwrite by
|
||||
/// older ancestors).
|
||||
#[test]
|
||||
fn ancestors_merge_in_chronological_order() {
|
||||
let key = B256::with_last_byte(1);
|
||||
// Oldest ancestor sets nonce to 1
|
||||
let oldest_state = HashedPostStateSorted::new(
|
||||
vec![(key, Some(Account { nonce: 1, balance: U256::ZERO, bytecode_hash: None }))],
|
||||
B256Map::default(),
|
||||
);
|
||||
// Newest ancestor overwrites nonce to 2
|
||||
let newest_state = HashedPostStateSorted::new(
|
||||
vec![(key, Some(Account { nonce: 2, balance: U256::ZERO, bytecode_hash: None }))],
|
||||
B256Map::default(),
|
||||
);
|
||||
|
||||
let oldest = ComputedTrieData {
|
||||
hashed_state: Arc::new(oldest_state),
|
||||
trie_updates: Arc::default(),
|
||||
anchored_trie_input: None,
|
||||
};
|
||||
let newest = ComputedTrieData {
|
||||
hashed_state: Arc::new(newest_state),
|
||||
trie_updates: Arc::default(),
|
||||
anchored_trie_input: None,
|
||||
};
|
||||
|
||||
// Pass ancestors oldest -> newest; newest should take precedence
|
||||
let deferred = DeferredTrieData::pending(
|
||||
Arc::new(HashedPostState::default()),
|
||||
Arc::new(TrieUpdates::default()),
|
||||
B256::ZERO,
|
||||
vec![DeferredTrieData::ready(oldest), DeferredTrieData::ready(newest)],
|
||||
);
|
||||
|
||||
let result = deferred.wait_cloned();
|
||||
let overlay_state = &result.anchored_trie_input.as_ref().unwrap().trie_input.state.accounts;
|
||||
assert_eq!(overlay_state.len(), 1);
|
||||
let (_, account) = &overlay_state[0];
|
||||
assert_eq!(account.unwrap().nonce, 2);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
CanonStateNotification, CanonStateNotificationSender, CanonStateNotifications,
|
||||
ChainInfoTracker, ComputedTrieData, DeferredTrieData, MemoryOverlayStateProvider,
|
||||
ChainInfoTracker, MemoryOverlayStateProvider,
|
||||
};
|
||||
use alloy_consensus::{transaction::TransactionMeta, BlockHeader};
|
||||
use alloy_eips::{BlockHashOrNumber, BlockNumHash};
|
||||
@@ -17,7 +17,7 @@ use reth_primitives_traits::{
|
||||
SignedTransaction,
|
||||
};
|
||||
use reth_storage_api::StateProviderBox;
|
||||
use reth_trie::{updates::TrieUpdatesSorted, HashedPostStateSorted, TrieInputSorted};
|
||||
use reth_trie::{updates::TrieUpdates, HashedPostState};
|
||||
use std::{collections::BTreeMap, sync::Arc, time::Instant};
|
||||
use tokio::sync::{broadcast, watch};
|
||||
|
||||
@@ -565,7 +565,7 @@ impl<N: NodePrimitives> CanonicalInMemoryState<N> {
|
||||
|
||||
/// State after applying the given block, this block is part of the canonical chain that partially
|
||||
/// stored in memory and can be traced back to a canonical block on disk.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, PartialEq, Eq, Clone)]
|
||||
pub struct BlockState<N: NodePrimitives = EthPrimitives> {
|
||||
/// The executed block that determines the state after this block has been executed.
|
||||
block: ExecutedBlock<N>,
|
||||
@@ -573,12 +573,6 @@ pub struct BlockState<N: NodePrimitives = EthPrimitives> {
|
||||
parent: Option<Arc<Self>>,
|
||||
}
|
||||
|
||||
impl<N: NodePrimitives> PartialEq for BlockState<N> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.block == other.block && self.parent == other.parent
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: NodePrimitives> BlockState<N> {
|
||||
/// [`BlockState`] constructor.
|
||||
pub const fn new(block: ExecutedBlock<N>) -> Self {
|
||||
@@ -725,17 +719,16 @@ impl<N: NodePrimitives> BlockState<N> {
|
||||
}
|
||||
|
||||
/// Represents an executed block stored in-memory.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ExecutedBlock<N: NodePrimitives = EthPrimitives> {
|
||||
/// Recovered Block
|
||||
pub recovered_block: Arc<RecoveredBlock<N::Block>>,
|
||||
/// Block's execution outcome.
|
||||
pub execution_output: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
/// Deferred trie data produced by execution.
|
||||
///
|
||||
/// This allows deferring the computation of the trie data which can be expensive.
|
||||
/// The data can be populated asynchronously after the block was validated.
|
||||
pub trie_data: DeferredTrieData,
|
||||
/// Block's hashed state.
|
||||
pub hashed_state: Arc<HashedPostState>,
|
||||
/// Trie updates that result from calculating the state root for the block.
|
||||
pub trie_updates: Arc<TrieUpdates>,
|
||||
}
|
||||
|
||||
impl<N: NodePrimitives> Default for ExecutedBlock<N> {
|
||||
@@ -743,54 +736,13 @@ impl<N: NodePrimitives> Default for ExecutedBlock<N> {
|
||||
Self {
|
||||
recovered_block: Default::default(),
|
||||
execution_output: Default::default(),
|
||||
trie_data: DeferredTrieData::ready(ComputedTrieData::default()),
|
||||
hashed_state: Default::default(),
|
||||
trie_updates: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: NodePrimitives> PartialEq for ExecutedBlock<N> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
// Trie data is computed asynchronously and doesn't define block identity.
|
||||
self.recovered_block == other.recovered_block &&
|
||||
self.execution_output == other.execution_output
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: NodePrimitives> ExecutedBlock<N> {
|
||||
/// Create a new [`ExecutedBlock`] with already-computed trie data.
|
||||
///
|
||||
/// Use this constructor when trie data is available immediately (e.g., sequencers,
|
||||
/// payload builders). This is the safe default path.
|
||||
pub fn new(
|
||||
recovered_block: Arc<RecoveredBlock<N::Block>>,
|
||||
execution_output: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
trie_data: ComputedTrieData,
|
||||
) -> Self {
|
||||
Self { recovered_block, execution_output, trie_data: DeferredTrieData::ready(trie_data) }
|
||||
}
|
||||
|
||||
/// Create a new [`ExecutedBlock`] with deferred trie data.
|
||||
///
|
||||
/// This is useful if the trie data is populated somewhere else, e.g. asynchronously
|
||||
/// after the block was validated.
|
||||
///
|
||||
/// The [`DeferredTrieData`] handle allows expensive trie operations (sorting hashed state,
|
||||
/// sorting trie updates, and building the accumulated trie input overlay) to be performed
|
||||
/// outside the critical validation path. This can improve latency for time-sensitive
|
||||
/// operations like block validation.
|
||||
///
|
||||
/// If the data hasn't been populated when [`Self::trie_data()`] is called, computation
|
||||
/// occurs synchronously from stored inputs, so there is no blocking or deadlock risk.
|
||||
///
|
||||
/// Use [`Self::new()`] instead when trie data is already computed and available immediately.
|
||||
pub const fn with_deferred_trie_data(
|
||||
recovered_block: Arc<RecoveredBlock<N::Block>>,
|
||||
execution_output: Arc<ExecutionOutcome<N::Receipt>>,
|
||||
trie_data: DeferredTrieData,
|
||||
) -> Self {
|
||||
Self { recovered_block, execution_output, trie_data }
|
||||
}
|
||||
|
||||
/// Returns a reference to an inner [`SealedBlock`]
|
||||
#[inline]
|
||||
pub fn sealed_block(&self) -> &SealedBlock<N::Block> {
|
||||
@@ -809,55 +761,16 @@ impl<N: NodePrimitives> ExecutedBlock<N> {
|
||||
&self.execution_output
|
||||
}
|
||||
|
||||
/// Returns the trie data, computing it synchronously if not already cached.
|
||||
///
|
||||
/// Uses `OnceLock::get_or_init` internally:
|
||||
/// - If already computed: returns cached result immediately
|
||||
/// - If not computed: first caller computes, others wait for that result
|
||||
/// Returns a reference to the hashed state result of the execution outcome
|
||||
#[inline]
|
||||
#[tracing::instrument(level = "debug", target = "engine::tree", name = "trie_data", skip_all)]
|
||||
pub fn trie_data(&self) -> ComputedTrieData {
|
||||
self.trie_data.wait_cloned()
|
||||
pub fn hashed_state(&self) -> &HashedPostState {
|
||||
&self.hashed_state
|
||||
}
|
||||
|
||||
/// Returns a clone of the deferred trie data handle.
|
||||
///
|
||||
/// A handle is a lightweight reference that can be passed to descendants without
|
||||
/// forcing trie data to be computed immediately. The actual work runs when
|
||||
/// `wait_cloned()` is called by a consumer (e.g. when merging overlays).
|
||||
/// Returns a reference to the trie updates resulting from the execution outcome
|
||||
#[inline]
|
||||
pub fn trie_data_handle(&self) -> DeferredTrieData {
|
||||
self.trie_data.clone()
|
||||
}
|
||||
|
||||
/// Returns the hashed state result of the execution outcome.
|
||||
///
|
||||
/// May compute trie data synchronously if the deferred task hasn't completed.
|
||||
#[inline]
|
||||
pub fn hashed_state(&self) -> Arc<HashedPostStateSorted> {
|
||||
self.trie_data().hashed_state
|
||||
}
|
||||
|
||||
/// Returns the trie updates resulting from the execution outcome.
|
||||
///
|
||||
/// May compute trie data synchronously if the deferred task hasn't completed.
|
||||
#[inline]
|
||||
pub fn trie_updates(&self) -> Arc<TrieUpdatesSorted> {
|
||||
self.trie_data().trie_updates
|
||||
}
|
||||
|
||||
/// Returns the trie input anchored to the persisted ancestor.
|
||||
///
|
||||
/// May compute trie data synchronously if the deferred task hasn't completed.
|
||||
#[inline]
|
||||
pub fn trie_input(&self) -> Option<Arc<TrieInputSorted>> {
|
||||
self.trie_data().trie_input().cloned()
|
||||
}
|
||||
|
||||
/// Returns the anchor hash of the trie input, if present.
|
||||
#[inline]
|
||||
pub fn anchor_hash(&self) -> Option<B256> {
|
||||
self.trie_data().anchor_hash()
|
||||
pub fn trie_updates(&self) -> &TrieUpdates {
|
||||
&self.trie_updates
|
||||
}
|
||||
|
||||
/// Returns a [`BlockNumber`] of the block.
|
||||
@@ -962,8 +875,8 @@ mod tests {
|
||||
StateProofProvider, StateProvider, StateRootProvider, StorageRootProvider,
|
||||
};
|
||||
use reth_trie::{
|
||||
updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof,
|
||||
MultiProofTargets, StorageMultiProof, StorageProof, TrieInput,
|
||||
AccountProof, HashedStorage, MultiProof, MultiProofTargets, StorageMultiProof,
|
||||
StorageProof, TrieInput,
|
||||
};
|
||||
|
||||
fn create_mock_state(
|
||||
|
||||
@@ -11,9 +11,6 @@
|
||||
mod in_memory;
|
||||
pub use in_memory::*;
|
||||
|
||||
mod deferred_trie;
|
||||
pub use deferred_trie::*;
|
||||
|
||||
mod noop;
|
||||
|
||||
mod chain_info;
|
||||
|
||||
@@ -53,10 +53,11 @@ impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> {
|
||||
/// Return lazy-loaded trie state aggregated from in-memory blocks.
|
||||
fn trie_input(&self) -> &TrieInput {
|
||||
self.trie_input.get_or_init(|| {
|
||||
let bundles: Vec<_> =
|
||||
self.in_memory.iter().rev().map(|block| block.trie_data()).collect();
|
||||
TrieInput::from_blocks_sorted(
|
||||
bundles.iter().map(|data| (data.hashed_state.as_ref(), data.trie_updates.as_ref())),
|
||||
TrieInput::from_blocks(
|
||||
self.in_memory
|
||||
.iter()
|
||||
.rev()
|
||||
.map(|block| (block.hashed_state.as_ref(), block.trie_updates.as_ref())),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
in_memory::ExecutedBlock, CanonStateNotification, CanonStateNotifications,
|
||||
CanonStateSubscriptions, ComputedTrieData,
|
||||
CanonStateSubscriptions,
|
||||
};
|
||||
use alloy_consensus::{Header, SignableTransaction, TxEip1559, TxReceipt, EMPTY_ROOT_HASH};
|
||||
use alloy_eips::{
|
||||
@@ -23,7 +23,7 @@ use reth_primitives_traits::{
|
||||
SignedTransaction,
|
||||
};
|
||||
use reth_storage_api::NodePrimitivesProvider;
|
||||
use reth_trie::root::state_root_unhashed;
|
||||
use reth_trie::{root::state_root_unhashed, updates::TrieUpdates, HashedPostState};
|
||||
use revm_database::BundleState;
|
||||
use revm_state::AccountInfo;
|
||||
use std::{
|
||||
@@ -92,7 +92,7 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
|
||||
&mut self,
|
||||
number: BlockNumber,
|
||||
parent_hash: B256,
|
||||
) -> SealedBlock<reth_ethereum_primitives::Block> {
|
||||
) -> RecoveredBlock<reth_ethereum_primitives::Block> {
|
||||
let mut rng = rand::rng();
|
||||
|
||||
let mock_tx = |nonce: u64| -> Recovered<_> {
|
||||
@@ -167,14 +167,17 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
SealedBlock::from_sealed_parts(
|
||||
let block = SealedBlock::from_sealed_parts(
|
||||
SealedHeader::seal_slow(header),
|
||||
BlockBody {
|
||||
transactions: transactions.into_iter().map(|tx| tx.into_inner()).collect(),
|
||||
ommers: Vec::new(),
|
||||
withdrawals: Some(vec![].into()),
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
RecoveredBlock::try_recover_sealed_with_senders(block, vec![self.signer; num_txs as usize])
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
/// Creates a fork chain with the given base block.
|
||||
@@ -188,9 +191,7 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
|
||||
|
||||
for _ in 0..length {
|
||||
let block = self.generate_random_block(parent.number + 1, parent.hash());
|
||||
parent = block.clone();
|
||||
let senders = vec![self.signer; block.body().transactions.len()];
|
||||
let block = block.with_senders(senders);
|
||||
parent = block.clone_sealed_block();
|
||||
fork.push(block);
|
||||
}
|
||||
|
||||
@@ -204,19 +205,20 @@ impl<N: NodePrimitives> TestBlockBuilder<N> {
|
||||
receipts: Vec<Vec<Receipt>>,
|
||||
parent_hash: B256,
|
||||
) -> ExecutedBlock {
|
||||
let block = self.generate_random_block(block_number, parent_hash);
|
||||
let senders = vec![self.signer; block.body().transactions.len()];
|
||||
let trie_data = ComputedTrieData::default();
|
||||
ExecutedBlock::new(
|
||||
Arc::new(RecoveredBlock::new_sealed(block, senders)),
|
||||
Arc::new(ExecutionOutcome::new(
|
||||
let block_with_senders = self.generate_random_block(block_number, parent_hash);
|
||||
|
||||
let (block, senders) = block_with_senders.split_sealed();
|
||||
ExecutedBlock {
|
||||
recovered_block: Arc::new(RecoveredBlock::new_sealed(block, senders)),
|
||||
execution_output: Arc::new(ExecutionOutcome::new(
|
||||
BundleState::default(),
|
||||
receipts,
|
||||
block_number,
|
||||
vec![Requests::default()],
|
||||
)),
|
||||
trie_data,
|
||||
)
|
||||
hashed_state: Arc::new(HashedPostState::default()),
|
||||
trie_updates: Arc::new(TrieUpdates::default()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates an [`ExecutedBlock`] that includes the given receipts.
|
||||
|
||||
@@ -30,9 +30,8 @@ pub use info::ChainInfo;
|
||||
#[cfg(any(test, feature = "test-utils"))]
|
||||
pub use spec::test_fork_ids;
|
||||
pub use spec::{
|
||||
blob_params_to_schedule, create_chain_config, mainnet_chain_config, make_genesis_header,
|
||||
BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder, ChainSpecProvider,
|
||||
DepositContract, ForkBaseFeeParams, DEV, HOLESKY, HOODI, MAINNET, SEPOLIA,
|
||||
make_genesis_header, BaseFeeParams, BaseFeeParamsKind, ChainSpec, ChainSpecBuilder,
|
||||
ChainSpecProvider, DepositContract, ForkBaseFeeParams, DEV, HOLESKY, HOODI, MAINNET, SEPOLIA,
|
||||
};
|
||||
|
||||
use reth_primitives_traits::sync::OnceLock;
|
||||
|
||||
@@ -10,14 +10,7 @@ use crate::{
|
||||
sepolia::SEPOLIA_PARIS_BLOCK,
|
||||
EthChainSpec,
|
||||
};
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::BTreeMap,
|
||||
format,
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
vec::Vec,
|
||||
};
|
||||
use alloc::{boxed::Box, format, sync::Arc, vec::Vec};
|
||||
use alloy_chains::{Chain, NamedChain};
|
||||
use alloy_consensus::{
|
||||
constants::{
|
||||
@@ -30,7 +23,7 @@ use alloy_eips::{
|
||||
eip1559::INITIAL_BASE_FEE, eip7685::EMPTY_REQUESTS_HASH, eip7840::BlobParams,
|
||||
eip7892::BlobScheduleBlobParams,
|
||||
};
|
||||
use alloy_genesis::{ChainConfig, Genesis};
|
||||
use alloy_genesis::Genesis;
|
||||
use alloy_primitives::{address, b256, Address, BlockNumber, B256, U256};
|
||||
use alloy_trie::root::state_root_ref_unhashed;
|
||||
use core::fmt::Debug;
|
||||
@@ -247,111 +240,6 @@ pub static DEV: LazyLock<Arc<ChainSpec>> = LazyLock::new(|| {
|
||||
.into()
|
||||
});
|
||||
|
||||
/// Creates a [`ChainConfig`] from the given chain, hardforks, deposit contract address, and blob
|
||||
/// schedule.
|
||||
pub fn create_chain_config(
|
||||
chain: Option<Chain>,
|
||||
hardforks: &ChainHardforks,
|
||||
deposit_contract_address: Option<Address>,
|
||||
blob_schedule: BTreeMap<String, BlobParams>,
|
||||
) -> ChainConfig {
|
||||
// Helper to extract block number from a hardfork condition
|
||||
let block_num = |fork: EthereumHardfork| hardforks.fork(fork).block_number();
|
||||
|
||||
// Helper to extract timestamp from a hardfork condition
|
||||
let timestamp = |fork: EthereumHardfork| -> Option<u64> {
|
||||
match hardforks.fork(fork) {
|
||||
ForkCondition::Timestamp(t) => Some(t),
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
|
||||
// Extract TTD from Paris fork
|
||||
let (terminal_total_difficulty, terminal_total_difficulty_passed) =
|
||||
match hardforks.fork(EthereumHardfork::Paris) {
|
||||
ForkCondition::TTD { total_difficulty, .. } => (Some(total_difficulty), true),
|
||||
_ => (None, false),
|
||||
};
|
||||
|
||||
// Check if DAO fork is supported (it has an activation block)
|
||||
let dao_fork_support = hardforks.fork(EthereumHardfork::Dao) != ForkCondition::Never;
|
||||
|
||||
ChainConfig {
|
||||
chain_id: chain.map(|c| c.id()).unwrap_or(0),
|
||||
homestead_block: block_num(EthereumHardfork::Homestead),
|
||||
dao_fork_block: block_num(EthereumHardfork::Dao),
|
||||
dao_fork_support,
|
||||
eip150_block: block_num(EthereumHardfork::Tangerine),
|
||||
eip155_block: block_num(EthereumHardfork::SpuriousDragon),
|
||||
eip158_block: block_num(EthereumHardfork::SpuriousDragon),
|
||||
byzantium_block: block_num(EthereumHardfork::Byzantium),
|
||||
constantinople_block: block_num(EthereumHardfork::Constantinople),
|
||||
petersburg_block: block_num(EthereumHardfork::Petersburg),
|
||||
istanbul_block: block_num(EthereumHardfork::Istanbul),
|
||||
muir_glacier_block: block_num(EthereumHardfork::MuirGlacier),
|
||||
berlin_block: block_num(EthereumHardfork::Berlin),
|
||||
london_block: block_num(EthereumHardfork::London),
|
||||
arrow_glacier_block: block_num(EthereumHardfork::ArrowGlacier),
|
||||
gray_glacier_block: block_num(EthereumHardfork::GrayGlacier),
|
||||
merge_netsplit_block: None,
|
||||
shanghai_time: timestamp(EthereumHardfork::Shanghai),
|
||||
cancun_time: timestamp(EthereumHardfork::Cancun),
|
||||
prague_time: timestamp(EthereumHardfork::Prague),
|
||||
osaka_time: timestamp(EthereumHardfork::Osaka),
|
||||
bpo1_time: timestamp(EthereumHardfork::Bpo1),
|
||||
bpo2_time: timestamp(EthereumHardfork::Bpo2),
|
||||
bpo3_time: timestamp(EthereumHardfork::Bpo3),
|
||||
bpo4_time: timestamp(EthereumHardfork::Bpo4),
|
||||
bpo5_time: timestamp(EthereumHardfork::Bpo5),
|
||||
terminal_total_difficulty,
|
||||
terminal_total_difficulty_passed,
|
||||
ethash: None,
|
||||
clique: None,
|
||||
parlia: None,
|
||||
extra_fields: Default::default(),
|
||||
deposit_contract_address,
|
||||
blob_schedule,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a [`ChainConfig`] for the current Ethereum mainnet chain.
|
||||
pub fn mainnet_chain_config() -> ChainConfig {
|
||||
let hardforks: ChainHardforks = EthereumHardfork::mainnet().into();
|
||||
let blob_schedule = blob_params_to_schedule(&MAINNET.blob_params, &hardforks);
|
||||
create_chain_config(
|
||||
Some(Chain::mainnet()),
|
||||
&hardforks,
|
||||
Some(MAINNET_DEPOSIT_CONTRACT.address),
|
||||
blob_schedule,
|
||||
)
|
||||
}
|
||||
|
||||
/// Converts the given [`BlobScheduleBlobParams`] into blobs schedule.
|
||||
pub fn blob_params_to_schedule(
|
||||
params: &BlobScheduleBlobParams,
|
||||
hardforks: &ChainHardforks,
|
||||
) -> BTreeMap<String, BlobParams> {
|
||||
let mut schedule = BTreeMap::new();
|
||||
schedule.insert("cancun".to_string(), params.cancun);
|
||||
schedule.insert("prague".to_string(), params.prague);
|
||||
schedule.insert("osaka".to_string(), params.osaka);
|
||||
|
||||
// Map scheduled entries back to bpo fork names by matching timestamps
|
||||
let bpo_forks = EthereumHardfork::bpo_variants();
|
||||
for (timestamp, blob_params) in ¶ms.scheduled {
|
||||
for bpo_fork in bpo_forks {
|
||||
if let ForkCondition::Timestamp(fork_ts) = hardforks.fork(bpo_fork) &&
|
||||
fork_ts == *timestamp
|
||||
{
|
||||
schedule.insert(bpo_fork.name().to_lowercase(), *blob_params);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
schedule
|
||||
}
|
||||
|
||||
/// A wrapper around [`BaseFeeParams`] that allows for specifying constant or dynamic EIP-1559
|
||||
/// parameters based on the active [Hardfork].
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
@@ -626,15 +514,8 @@ impl<H: BlockHeader> ChainSpec<H> {
|
||||
|
||||
/// Compute the [`ForkId`] for the given [`Head`] following eip-6122 spec.
|
||||
///
|
||||
/// The fork hash is computed by starting from the genesis hash and iteratively adding
|
||||
/// block numbers (for block-based forks) or timestamps (for timestamp-based forks) of
|
||||
/// active forks. The `next` field indicates the next fork activation point, or `0` if
|
||||
/// all forks are active.
|
||||
///
|
||||
/// Block-based forks are processed first, then timestamp-based forks. Multiple hardforks
|
||||
/// activated at the same block or timestamp: only the first one is applied.
|
||||
///
|
||||
/// See: <https://eips.ethereum.org/EIPS/eip-6122>
|
||||
/// Note: In case there are multiple hardforks activated at the same block or timestamp, only
|
||||
/// the first gets applied.
|
||||
pub fn fork_id(&self, head: &Head) -> ForkId {
|
||||
let mut forkhash = ForkHash::from(self.genesis_hash());
|
||||
|
||||
@@ -691,10 +572,6 @@ impl<H: BlockHeader> ChainSpec<H> {
|
||||
}
|
||||
|
||||
/// An internal helper function that returns a head block that satisfies a given Fork condition.
|
||||
///
|
||||
/// Creates a [`Head`] representation for a fork activation point, used by [`Self::fork_id`] to
|
||||
/// compute fork IDs. For timestamp-based forks, includes the last block-based fork number
|
||||
/// before the merge (if any).
|
||||
pub(crate) fn satisfy(&self, cond: ForkCondition) -> Head {
|
||||
match cond {
|
||||
ForkCondition::Block(number) => Head { number, ..Default::default() },
|
||||
@@ -1048,16 +925,9 @@ impl ChainSpecBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Dao at genesis.
|
||||
pub fn dao_activated(mut self) -> Self {
|
||||
self = self.frontier_activated();
|
||||
self.hardforks.insert(EthereumHardfork::Dao, ForkCondition::Block(0));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Homestead at genesis.
|
||||
pub fn homestead_activated(mut self) -> Self {
|
||||
self = self.dao_activated();
|
||||
self = self.frontier_activated();
|
||||
self.hardforks.insert(EthereumHardfork::Homestead, ForkCondition::Block(0));
|
||||
self
|
||||
}
|
||||
@@ -1104,16 +974,9 @@ impl ChainSpecBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Muir Glacier at genesis.
|
||||
pub fn muirglacier_activated(mut self) -> Self {
|
||||
self = self.istanbul_activated();
|
||||
self.hardforks.insert(EthereumHardfork::MuirGlacier, ForkCondition::Block(0));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Berlin at genesis.
|
||||
pub fn berlin_activated(mut self) -> Self {
|
||||
self = self.muirglacier_activated();
|
||||
self = self.istanbul_activated();
|
||||
self.hardforks.insert(EthereumHardfork::Berlin, ForkCondition::Block(0));
|
||||
self
|
||||
}
|
||||
@@ -1125,23 +988,9 @@ impl ChainSpecBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Arrow Glacier at genesis.
|
||||
pub fn arrowglacier_activated(mut self) -> Self {
|
||||
self = self.london_activated();
|
||||
self.hardforks.insert(EthereumHardfork::ArrowGlacier, ForkCondition::Block(0));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Gray Glacier at genesis.
|
||||
pub fn grayglacier_activated(mut self) -> Self {
|
||||
self = self.arrowglacier_activated();
|
||||
self.hardforks.insert(EthereumHardfork::GrayGlacier, ForkCondition::Block(0));
|
||||
self
|
||||
}
|
||||
|
||||
/// Enable Paris at genesis.
|
||||
pub fn paris_activated(mut self) -> Self {
|
||||
self = self.grayglacier_activated();
|
||||
self = self.london_activated();
|
||||
self.hardforks.insert(
|
||||
EthereumHardfork::Paris,
|
||||
ForkCondition::TTD {
|
||||
@@ -1514,72 +1363,72 @@ Post-merge hard forks (timestamp based):
|
||||
&[
|
||||
(
|
||||
EthereumHardfork::Frontier,
|
||||
ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
|
||||
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Homestead,
|
||||
ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
|
||||
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Dao,
|
||||
ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
|
||||
ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Tangerine,
|
||||
ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
|
||||
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::SpuriousDragon,
|
||||
ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
|
||||
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Byzantium,
|
||||
ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
|
||||
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Constantinople,
|
||||
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
|
||||
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Petersburg,
|
||||
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
|
||||
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Istanbul,
|
||||
ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
|
||||
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::MuirGlacier,
|
||||
ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
|
||||
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Berlin,
|
||||
ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
|
||||
ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::London,
|
||||
ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
|
||||
ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::ArrowGlacier,
|
||||
ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
|
||||
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::GrayGlacier,
|
||||
ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
|
||||
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Shanghai,
|
||||
ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
|
||||
ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Cancun,
|
||||
ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
|
||||
ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Prague,
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0xc376cf8b")),
|
||||
hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]),
|
||||
next: mainnet::MAINNET_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
@@ -1594,60 +1443,60 @@ Post-merge hard forks (timestamp based):
|
||||
&[
|
||||
(
|
||||
EthereumHardfork::Frontier,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Homestead,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Tangerine,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::SpuriousDragon,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Byzantium,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Constantinople,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Petersburg,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Istanbul,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Berlin,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::London,
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Paris,
|
||||
ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
|
||||
ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Shanghai,
|
||||
ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
|
||||
ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Cancun,
|
||||
ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
|
||||
ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
|
||||
),
|
||||
(
|
||||
EthereumHardfork::Prague,
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0xed88b5fd")),
|
||||
hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]),
|
||||
next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
@@ -1662,71 +1511,71 @@ Post-merge hard forks (timestamp based):
|
||||
&[
|
||||
(
|
||||
Head { number: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
|
||||
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
|
||||
),
|
||||
(
|
||||
Head { number: 1150000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
|
||||
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
|
||||
),
|
||||
(
|
||||
Head { number: 1920000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
|
||||
ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
|
||||
),
|
||||
(
|
||||
Head { number: 2463000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
|
||||
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
|
||||
),
|
||||
(
|
||||
Head { number: 2675000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
|
||||
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
|
||||
),
|
||||
(
|
||||
Head { number: 4370000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
|
||||
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
|
||||
),
|
||||
(
|
||||
Head { number: 7280000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
|
||||
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
|
||||
),
|
||||
(
|
||||
Head { number: 9069000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
|
||||
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
|
||||
),
|
||||
(
|
||||
Head { number: 9200000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
|
||||
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
|
||||
),
|
||||
(
|
||||
Head { number: 12244000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
|
||||
ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
|
||||
),
|
||||
(
|
||||
Head { number: 12965000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
|
||||
ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
|
||||
),
|
||||
(
|
||||
Head { number: 13773000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
|
||||
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
|
||||
),
|
||||
(
|
||||
Head { number: 15050000, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
|
||||
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
|
||||
),
|
||||
// First Shanghai block
|
||||
(
|
||||
Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
|
||||
ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
|
||||
),
|
||||
// First Cancun block
|
||||
(
|
||||
Head { number: 20000001, timestamp: 1710338135, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
|
||||
ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
|
||||
),
|
||||
// First Prague block
|
||||
(
|
||||
Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0xc376cf8b")),
|
||||
hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]),
|
||||
next: mainnet::MAINNET_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
@@ -1753,13 +1602,13 @@ Post-merge hard forks (timestamp based):
|
||||
&[
|
||||
(
|
||||
Head { number: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xbef71d30")), next: 1742999832 },
|
||||
ForkId { hash: ForkHash([0xbe, 0xf7, 0x1d, 0x30]), next: 1742999832 },
|
||||
),
|
||||
// First Prague block
|
||||
(
|
||||
Head { number: 0, timestamp: 1742999833, ..Default::default() },
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0x0929e24e")),
|
||||
hash: ForkHash([0x09, 0x29, 0xe2, 0x4e]),
|
||||
next: hoodi::HOODI_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
@@ -1786,43 +1635,43 @@ Post-merge hard forks (timestamp based):
|
||||
&[
|
||||
(
|
||||
Head { number: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
|
||||
ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
|
||||
),
|
||||
// First MergeNetsplit block
|
||||
(
|
||||
Head { number: 123, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
|
||||
ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
|
||||
),
|
||||
// Last MergeNetsplit block
|
||||
(
|
||||
Head { number: 123, timestamp: 1696000703, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xc61a6098")), next: 1696000704 },
|
||||
ForkId { hash: ForkHash([0xc6, 0x1a, 0x60, 0x98]), next: 1696000704 },
|
||||
),
|
||||
// First Shanghai block
|
||||
(
|
||||
Head { number: 123, timestamp: 1696000704, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xfd4f016b")), next: 1707305664 },
|
||||
ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
|
||||
),
|
||||
// Last Shanghai block
|
||||
(
|
||||
Head { number: 123, timestamp: 1707305663, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xfd4f016b")), next: 1707305664 },
|
||||
ForkId { hash: ForkHash([0xfd, 0x4f, 0x01, 0x6b]), next: 1707305664 },
|
||||
),
|
||||
// First Cancun block
|
||||
(
|
||||
Head { number: 123, timestamp: 1707305664, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x9b192ad0")), next: 1740434112 },
|
||||
ForkId { hash: ForkHash([0x9b, 0x19, 0x2a, 0xd0]), next: 1740434112 },
|
||||
),
|
||||
// Last Cancun block
|
||||
(
|
||||
Head { number: 123, timestamp: 1740434111, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x9b192ad0")), next: 1740434112 },
|
||||
ForkId { hash: ForkHash([0x9b, 0x19, 0x2a, 0xd0]), next: 1740434112 },
|
||||
),
|
||||
// First Prague block
|
||||
(
|
||||
Head { number: 123, timestamp: 1740434112, ..Default::default() },
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0xdfbd9bed")),
|
||||
hash: ForkHash([0xdf, 0xbd, 0x9b, 0xed]),
|
||||
next: holesky::HOLESKY_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
@@ -1849,45 +1698,45 @@ Post-merge hard forks (timestamp based):
|
||||
&[
|
||||
(
|
||||
Head { number: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
Head { number: 1735370, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xfe3366e7")), next: 1735371 },
|
||||
ForkId { hash: ForkHash([0xfe, 0x33, 0x66, 0xe7]), next: 1735371 },
|
||||
),
|
||||
(
|
||||
Head { number: 1735371, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
|
||||
ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
|
||||
),
|
||||
(
|
||||
Head { number: 1735372, timestamp: 1677557087, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xb96cbd13")), next: 1677557088 },
|
||||
ForkId { hash: ForkHash([0xb9, 0x6c, 0xbd, 0x13]), next: 1677557088 },
|
||||
),
|
||||
// First Shanghai block
|
||||
(
|
||||
Head { number: 1735373, timestamp: 1677557088, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
|
||||
ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
|
||||
),
|
||||
// Last Shanghai block
|
||||
(
|
||||
Head { number: 1735374, timestamp: 1706655071, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xf7f9bc08")), next: 1706655072 },
|
||||
ForkId { hash: ForkHash([0xf7, 0xf9, 0xbc, 0x08]), next: 1706655072 },
|
||||
),
|
||||
// First Cancun block
|
||||
(
|
||||
Head { number: 1735375, timestamp: 1706655072, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
|
||||
ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
|
||||
),
|
||||
// Last Cancun block
|
||||
(
|
||||
Head { number: 1735376, timestamp: 1741159775, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x88cf81d9")), next: 1741159776 },
|
||||
ForkId { hash: ForkHash([0x88, 0xcf, 0x81, 0xd9]), next: 1741159776 },
|
||||
),
|
||||
// First Prague block
|
||||
(
|
||||
Head { number: 1735377, timestamp: 1741159776, ..Default::default() },
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0xed88b5fd")),
|
||||
hash: ForkHash([0xed, 0x88, 0xb5, 0xfd]),
|
||||
next: sepolia::SEPOLIA_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
@@ -1913,7 +1762,7 @@ Post-merge hard forks (timestamp based):
|
||||
&DEV,
|
||||
&[(
|
||||
Head { number: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x0b1a4ef7")), next: 0 },
|
||||
ForkId { hash: ForkHash([0x0b, 0x1a, 0x4e, 0xf7]), next: 0 },
|
||||
)],
|
||||
)
|
||||
}
|
||||
@@ -1929,128 +1778,128 @@ Post-merge hard forks (timestamp based):
|
||||
&[
|
||||
(
|
||||
Head { number: 0, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
|
||||
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
|
||||
), // Unsynced
|
||||
(
|
||||
Head { number: 1149999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xfc64ec04")), next: 1150000 },
|
||||
ForkId { hash: ForkHash([0xfc, 0x64, 0xec, 0x04]), next: 1150000 },
|
||||
), // Last Frontier block
|
||||
(
|
||||
Head { number: 1150000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
|
||||
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
|
||||
), // First Homestead block
|
||||
(
|
||||
Head { number: 1919999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x97c2c34c")), next: 1920000 },
|
||||
ForkId { hash: ForkHash([0x97, 0xc2, 0xc3, 0x4c]), next: 1920000 },
|
||||
), // Last Homestead block
|
||||
(
|
||||
Head { number: 1920000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
|
||||
ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
|
||||
), // First DAO block
|
||||
(
|
||||
Head { number: 2462999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x91d1f948")), next: 2463000 },
|
||||
ForkId { hash: ForkHash([0x91, 0xd1, 0xf9, 0x48]), next: 2463000 },
|
||||
), // Last DAO block
|
||||
(
|
||||
Head { number: 2463000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
|
||||
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
|
||||
), // First Tangerine block
|
||||
(
|
||||
Head { number: 2674999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x7a64da13")), next: 2675000 },
|
||||
ForkId { hash: ForkHash([0x7a, 0x64, 0xda, 0x13]), next: 2675000 },
|
||||
), // Last Tangerine block
|
||||
(
|
||||
Head { number: 2675000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
|
||||
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
|
||||
), // First Spurious block
|
||||
(
|
||||
Head { number: 4369999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x3edd5b10")), next: 4370000 },
|
||||
ForkId { hash: ForkHash([0x3e, 0xdd, 0x5b, 0x10]), next: 4370000 },
|
||||
), // Last Spurious block
|
||||
(
|
||||
Head { number: 4370000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
|
||||
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
|
||||
), // First Byzantium block
|
||||
(
|
||||
Head { number: 7279999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xa00bc324")), next: 7280000 },
|
||||
ForkId { hash: ForkHash([0xa0, 0x0b, 0xc3, 0x24]), next: 7280000 },
|
||||
), // Last Byzantium block
|
||||
(
|
||||
Head { number: 7280000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
|
||||
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
|
||||
), // First and last Constantinople, first Petersburg block
|
||||
(
|
||||
Head { number: 9068999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x668db0af")), next: 9069000 },
|
||||
ForkId { hash: ForkHash([0x66, 0x8d, 0xb0, 0xaf]), next: 9069000 },
|
||||
), // Last Petersburg block
|
||||
(
|
||||
Head { number: 9069000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
|
||||
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
|
||||
), // First Istanbul and first Muir Glacier block
|
||||
(
|
||||
Head { number: 9199999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x879d6e30")), next: 9200000 },
|
||||
ForkId { hash: ForkHash([0x87, 0x9d, 0x6e, 0x30]), next: 9200000 },
|
||||
), // Last Istanbul and first Muir Glacier block
|
||||
(
|
||||
Head { number: 9200000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
|
||||
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
|
||||
), // First Muir Glacier block
|
||||
(
|
||||
Head { number: 12243999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xe029e991")), next: 12244000 },
|
||||
ForkId { hash: ForkHash([0xe0, 0x29, 0xe9, 0x91]), next: 12244000 },
|
||||
), // Last Muir Glacier block
|
||||
(
|
||||
Head { number: 12244000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
|
||||
ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
|
||||
), // First Berlin block
|
||||
(
|
||||
Head { number: 12964999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x0eb440f6")), next: 12965000 },
|
||||
ForkId { hash: ForkHash([0x0e, 0xb4, 0x40, 0xf6]), next: 12965000 },
|
||||
), // Last Berlin block
|
||||
(
|
||||
Head { number: 12965000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
|
||||
ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
|
||||
), // First London block
|
||||
(
|
||||
Head { number: 13772999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xb715077d")), next: 13773000 },
|
||||
ForkId { hash: ForkHash([0xb7, 0x15, 0x07, 0x7d]), next: 13773000 },
|
||||
), // Last London block
|
||||
(
|
||||
Head { number: 13773000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
|
||||
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
|
||||
), // First Arrow Glacier block
|
||||
(
|
||||
Head { number: 15049999, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x20c327fc")), next: 15050000 },
|
||||
ForkId { hash: ForkHash([0x20, 0xc3, 0x27, 0xfc]), next: 15050000 },
|
||||
), // Last Arrow Glacier block
|
||||
(
|
||||
Head { number: 15050000, timestamp: 0, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
|
||||
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
|
||||
), // First Gray Glacier block
|
||||
(
|
||||
Head { number: 19999999, timestamp: 1667999999, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xf0afd0e3")), next: 1681338455 },
|
||||
ForkId { hash: ForkHash([0xf0, 0xaf, 0xd0, 0xe3]), next: 1681338455 },
|
||||
), // Last Gray Glacier block
|
||||
(
|
||||
Head { number: 20000000, timestamp: 1681338455, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
|
||||
ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
|
||||
), // Last Shanghai block
|
||||
(
|
||||
Head { number: 20000001, timestamp: 1710338134, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0xdce96c2d")), next: 1710338135 },
|
||||
ForkId { hash: ForkHash([0xdc, 0xe9, 0x6c, 0x2d]), next: 1710338135 },
|
||||
), // First Cancun block
|
||||
(
|
||||
Head { number: 20000002, timestamp: 1710338135, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
|
||||
ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
|
||||
), // Last Cancun block
|
||||
(
|
||||
Head { number: 20000003, timestamp: 1746612310, ..Default::default() },
|
||||
ForkId { hash: ForkHash(hex!("0x9f3d2254")), next: 1746612311 },
|
||||
ForkId { hash: ForkHash([0x9f, 0x3d, 0x22, 0x54]), next: 1746612311 },
|
||||
), // First Prague block
|
||||
(
|
||||
Head { number: 20000004, timestamp: 1746612311, ..Default::default() },
|
||||
ForkId {
|
||||
hash: ForkHash(hex!("0xc376cf8b")),
|
||||
hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]),
|
||||
next: mainnet::MAINNET_OSAKA_TIMESTAMP,
|
||||
},
|
||||
),
|
||||
@@ -2520,7 +2369,7 @@ Post-merge hard forks (timestamp based):
|
||||
let chainspec = ChainSpec::from(genesis);
|
||||
|
||||
// make sure we are at ForkHash("bc0c2605") with Head post-cancun
|
||||
let expected_forkid = ForkId { hash: ForkHash(hex!("0xbc0c2605")), next: 0 };
|
||||
let expected_forkid = ForkId { hash: ForkHash([0xbc, 0x0c, 0x26, 0x05]), next: 0 };
|
||||
let got_forkid =
|
||||
chainspec.fork_id(&Head { number: 73, timestamp: 840, ..Default::default() });
|
||||
|
||||
@@ -2630,7 +2479,7 @@ Post-merge hard forks (timestamp based):
|
||||
assert_eq!(genesis_hash, expected_hash);
|
||||
|
||||
// check that the forkhash is correct
|
||||
let expected_forkhash = ForkHash(hex!("0x8062457a"));
|
||||
let expected_forkhash = ForkHash(hex!("8062457a"));
|
||||
assert_eq!(ForkHash::from(genesis_hash), expected_forkhash);
|
||||
}
|
||||
|
||||
|
||||
@@ -49,7 +49,6 @@ reth-stages.workspace = true
|
||||
reth-stages-types = { workspace = true, optional = true }
|
||||
reth-static-file-types = { workspace = true, features = ["clap"] }
|
||||
reth-static-file.workspace = true
|
||||
reth-tasks.workspace = true
|
||||
reth-trie = { workspace = true, features = ["metrics"] }
|
||||
reth-trie-db = { workspace = true, features = ["metrics"] }
|
||||
reth-trie-common.workspace = true
|
||||
@@ -83,7 +82,6 @@ backon.workspace = true
|
||||
secp256k1 = { workspace = true, features = ["global-context", "std", "recovery"] }
|
||||
tokio-stream.workspace = true
|
||||
reqwest.workspace = true
|
||||
metrics.workspace = true
|
||||
|
||||
# io
|
||||
fdlimit.workspace = true
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
//! Contains common `reth` arguments
|
||||
|
||||
pub use reth_primitives_traits::header::HeaderMut;
|
||||
|
||||
use alloy_primitives::B256;
|
||||
use clap::Parser;
|
||||
use reth_chainspec::EthChainSpec;
|
||||
@@ -9,7 +7,7 @@ use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_config::{config::EtlConfig, Config};
|
||||
use reth_consensus::noop::NoopConsensus;
|
||||
use reth_db::{init_db, open_db_read_only, DatabaseEnv};
|
||||
use reth_db_common::init::init_genesis_with_settings;
|
||||
use reth_db_common::init::init_genesis;
|
||||
use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader};
|
||||
use reth_eth_wire::NetPrimitivesFor;
|
||||
use reth_evm::{noop::NoopEvmConfig, ConfigureEvm};
|
||||
@@ -19,7 +17,7 @@ use reth_node_builder::{
|
||||
Node, NodeComponents, NodeComponentsBuilder, NodeTypes, NodeTypesWithDBAdapter,
|
||||
};
|
||||
use reth_node_core::{
|
||||
args::{DatabaseArgs, DatadirArgs, StaticFilesArgs},
|
||||
args::{DatabaseArgs, DatadirArgs},
|
||||
dirs::{ChainPath, DataDirPath},
|
||||
};
|
||||
use reth_provider::{
|
||||
@@ -59,10 +57,6 @@ pub struct EnvironmentArgs<C: ChainSpecParser> {
|
||||
/// All database related arguments
|
||||
#[command(flatten)]
|
||||
pub db: DatabaseArgs,
|
||||
|
||||
/// All static files related arguments
|
||||
#[command(flatten)]
|
||||
pub static_files: StaticFilesArgs,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser> EnvironmentArgs<C> {
|
||||
@@ -103,16 +97,16 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
|
||||
Arc::new(init_db(db_path, self.db.database_args())?),
|
||||
StaticFileProvider::read_write(sf_path)?,
|
||||
),
|
||||
AccessRights::RO | AccessRights::RoInconsistent => (
|
||||
AccessRights::RO => (
|
||||
Arc::new(open_db_read_only(&db_path, self.db.database_args())?),
|
||||
StaticFileProvider::read_only(sf_path, false)?,
|
||||
),
|
||||
};
|
||||
|
||||
let provider_factory = self.create_provider_factory(&config, db, sfp, access)?;
|
||||
let provider_factory = self.create_provider_factory(&config, db, sfp)?;
|
||||
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())?;
|
||||
init_genesis(&provider_factory)?;
|
||||
}
|
||||
|
||||
Ok(Environment { config, provider_factory, data_dir })
|
||||
@@ -128,23 +122,23 @@ impl<C: ChainSpecParser> EnvironmentArgs<C> {
|
||||
config: &Config,
|
||||
db: Arc<DatabaseEnv>,
|
||||
static_file_provider: StaticFileProvider<N::Primitives>,
|
||||
access: AccessRights,
|
||||
) -> eyre::Result<ProviderFactory<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>>
|
||||
where
|
||||
C: ChainSpecParser<ChainSpec = N::ChainSpec>,
|
||||
{
|
||||
let has_receipt_pruning = config.prune.has_receipts_pruning();
|
||||
let prune_modes = config.prune.segments.clone();
|
||||
let factory = ProviderFactory::<NodeTypesWithDBAdapter<N, Arc<DatabaseEnv>>>::new(
|
||||
db,
|
||||
self.chain.clone(),
|
||||
static_file_provider,
|
||||
)?
|
||||
)
|
||||
.with_prune_modes(prune_modes.clone());
|
||||
|
||||
// Check for consistency between database and static files.
|
||||
if !access.is_read_only_inconsistent() &&
|
||||
let Some(unwind_target) =
|
||||
factory.static_file_provider().check_consistency(&factory.provider()?)?
|
||||
if let Some(unwind_target) = factory
|
||||
.static_file_provider()
|
||||
.check_consistency(&factory.provider()?, has_receipt_pruning)?
|
||||
{
|
||||
if factory.db_ref().is_read_only()? {
|
||||
warn!(target: "reth::cli", ?unwind_target, "Inconsistent storage. Restart node to heal.");
|
||||
@@ -205,8 +199,6 @@ pub enum AccessRights {
|
||||
RW,
|
||||
/// Read-only access
|
||||
RO,
|
||||
/// Read-only access with possibly inconsistent data
|
||||
RoInconsistent,
|
||||
}
|
||||
|
||||
impl AccessRights {
|
||||
@@ -214,12 +206,6 @@ impl AccessRights {
|
||||
pub const fn is_read_write(&self) -> bool {
|
||||
matches!(self, Self::RW)
|
||||
}
|
||||
|
||||
/// Returns `true` if it requires read-only access to the environment with possibly inconsistent
|
||||
/// data.
|
||||
pub const fn is_read_only_inconsistent(&self) -> bool {
|
||||
matches!(self, Self::RoInconsistent)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper alias to satisfy `FullNodeTypes` bound on [`Node`] trait generic.
|
||||
@@ -229,6 +215,17 @@ type FullTypesAdapter<T> = FullNodeTypesAdapter<
|
||||
BlockchainProvider<NodeTypesWithDBAdapter<T, Arc<DatabaseEnv>>>,
|
||||
>;
|
||||
|
||||
/// Trait for block headers that can be modified through CLI operations.
|
||||
pub trait CliHeader {
|
||||
fn set_number(&mut self, number: u64);
|
||||
}
|
||||
|
||||
impl CliHeader for alloy_consensus::Header {
|
||||
fn set_number(&mut self, number: u64) {
|
||||
self.number = number;
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper trait with a common set of requirements for the
|
||||
/// [`NodeTypes`] in CLI.
|
||||
pub trait CliNodeTypes: Node<FullTypesAdapter<Self>> + NodeTypesForProvider {
|
||||
|
||||
@@ -22,14 +22,13 @@ impl Command {
|
||||
let config = if self.default {
|
||||
Config::default()
|
||||
} else {
|
||||
let path = match self.config.as_ref() {
|
||||
Some(path) => path,
|
||||
None => bail!("No config file provided. Use --config <FILE> or pass --default"),
|
||||
};
|
||||
let path = self.config.clone().unwrap_or_default();
|
||||
// Check if the file exists
|
||||
if !path.exists() {
|
||||
bail!("Config file does not exist: {}", path.display());
|
||||
}
|
||||
Config::from_path(path)
|
||||
// Read the configuration file
|
||||
Config::from_path(&path)
|
||||
.wrap_err_with(|| format!("Could not load config file: {}", path.display()))?
|
||||
};
|
||||
println!("{}", toml::to_string_pretty(&config)?);
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
use alloy_primitives::{keccak256, Address};
|
||||
use clap::Parser;
|
||||
use human_bytes::human_bytes;
|
||||
use reth_codecs::Compact;
|
||||
use reth_db_api::{cursor::DbDupCursorRO, database::Database, tables, transaction::DbTx};
|
||||
use reth_db_common::DbTool;
|
||||
use reth_node_builder::NodeTypesWithDB;
|
||||
use std::time::{Duration, Instant};
|
||||
use tracing::info;
|
||||
|
||||
/// Log progress every 5 seconds
|
||||
const LOG_INTERVAL: Duration = Duration::from_secs(5);
|
||||
|
||||
/// The arguments for the `reth db account-storage` command
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct Command {
|
||||
/// The account address to check storage for
|
||||
address: Address,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Execute `db account-storage` command
|
||||
pub fn execute<N: NodeTypesWithDB>(self, tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
let address = self.address;
|
||||
let (slot_count, plain_size) = tool.provider_factory.db_ref().view(|tx| {
|
||||
let mut cursor = tx.cursor_dup_read::<tables::PlainStorageState>()?;
|
||||
let mut count = 0usize;
|
||||
let mut total_value_bytes = 0usize;
|
||||
let mut last_log = Instant::now();
|
||||
|
||||
// Walk all storage entries for this address
|
||||
let walker = cursor.walk_dup(Some(address), None)?;
|
||||
for entry in walker {
|
||||
let (_, storage_entry) = entry?;
|
||||
count += 1;
|
||||
// StorageEntry encodes as: 32 bytes (key/subkey uncompressed) + compressed U256
|
||||
let mut buf = Vec::new();
|
||||
let entry_len = storage_entry.to_compact(&mut buf);
|
||||
total_value_bytes += entry_len;
|
||||
|
||||
if last_log.elapsed() >= LOG_INTERVAL {
|
||||
info!(
|
||||
target: "reth::cli",
|
||||
address = %address,
|
||||
slots = count,
|
||||
key = %storage_entry.key,
|
||||
"Processing storage slots"
|
||||
);
|
||||
last_log = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
// Add 20 bytes for the Address key (stored once per account in dupsort)
|
||||
let total_size = if count > 0 { 20 + total_value_bytes } else { 0 };
|
||||
|
||||
Ok::<_, eyre::Report>((count, total_size))
|
||||
})??;
|
||||
|
||||
// Estimate hashed storage size: 32-byte B256 key instead of 20-byte Address
|
||||
let hashed_size_estimate = if slot_count > 0 { plain_size + 12 } else { 0 };
|
||||
let total_estimate = plain_size + hashed_size_estimate;
|
||||
|
||||
let hashed_address = keccak256(address);
|
||||
|
||||
println!("Account: {address}");
|
||||
println!("Hashed address: {hashed_address}");
|
||||
println!("Storage slots: {slot_count}");
|
||||
println!("Plain storage size: {} (estimated)", human_bytes(plain_size as f64));
|
||||
println!("Hashed storage size: {} (estimated)", human_bytes(hashed_size_estimate as f64));
|
||||
println!("Total estimated size: {}", human_bytes(total_estimate as f64));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_address_arg() {
|
||||
let cmd = Command::try_parse_from([
|
||||
"account-storage",
|
||||
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
|
||||
])
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
cmd.address,
|
||||
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".parse::<Address>().unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -6,9 +6,8 @@ use reth_db_api::{
|
||||
transaction::{DbTx, DbTxMut},
|
||||
TableViewer, Tables,
|
||||
};
|
||||
use reth_db_common::DbTool;
|
||||
use reth_node_builder::NodeTypesWithDB;
|
||||
use reth_provider::StaticFileProviderFactory;
|
||||
use reth_provider::{ProviderFactory, StaticFileProviderFactory};
|
||||
use reth_static_file_types::StaticFileSegment;
|
||||
|
||||
/// The arguments for the `reth db clear` command
|
||||
@@ -20,13 +19,16 @@ pub struct Command {
|
||||
|
||||
impl Command {
|
||||
/// Execute `db clear` command
|
||||
pub fn execute<N: NodeTypesWithDB>(self, tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
pub fn execute<N: NodeTypesWithDB>(
|
||||
self,
|
||||
provider_factory: ProviderFactory<N>,
|
||||
) -> eyre::Result<()> {
|
||||
match self.subcommand {
|
||||
Subcommands::Mdbx { table } => {
|
||||
table.view(&ClearViewer { db: tool.provider_factory.db_ref() })?
|
||||
table.view(&ClearViewer { db: provider_factory.db_ref() })?
|
||||
}
|
||||
Subcommands::StaticFile { segment } => {
|
||||
let static_file_provider = tool.provider_factory.static_file_provider();
|
||||
let static_file_provider = provider_factory.static_file_provider();
|
||||
let static_files = iter_static_files(static_file_provider.directory())?;
|
||||
|
||||
if let Some(segment_static_files) = static_files.get(&segment) {
|
||||
|
||||
@@ -3,7 +3,6 @@ use clap::Parser;
|
||||
use reth_db::{
|
||||
static_file::{
|
||||
ColumnSelectorOne, ColumnSelectorTwo, HeaderWithHashMask, ReceiptMask, TransactionMask,
|
||||
TransactionSenderMask,
|
||||
},
|
||||
RawDupSort,
|
||||
};
|
||||
@@ -76,21 +75,19 @@ impl Command {
|
||||
StaticFileSegment::Receipts => {
|
||||
(table_key::<tables::Receipts>(&key)?, <ReceiptMask<ReceiptTy<N>>>::MASK)
|
||||
}
|
||||
StaticFileSegment::TransactionSenders => (
|
||||
table_key::<tables::TransactionSenders>(&key)?,
|
||||
<TransactionSenderMask>::MASK,
|
||||
),
|
||||
};
|
||||
|
||||
let content = tool
|
||||
.provider_factory
|
||||
.static_file_provider()
|
||||
.get_segment_provider(segment, key)?
|
||||
.cursor()?
|
||||
.get(key.into(), mask)
|
||||
.map(|result| {
|
||||
result.map(|vec| vec.iter().map(|slice| slice.to_vec()).collect::<Vec<_>>())
|
||||
})?;
|
||||
let content = tool.provider_factory.static_file_provider().find_static_file(
|
||||
segment,
|
||||
|provider| {
|
||||
let mut cursor = provider.cursor()?;
|
||||
cursor.get(key.into(), mask).map(|result| {
|
||||
result.map(|vec| {
|
||||
vec.iter().map(|slice| slice.to_vec()).collect::<Vec<_>>()
|
||||
})
|
||||
})
|
||||
},
|
||||
)?;
|
||||
|
||||
match content {
|
||||
Some(content) => {
|
||||
@@ -119,13 +116,6 @@ impl Command {
|
||||
)?;
|
||||
println!("{}", serde_json::to_string_pretty(&receipt)?);
|
||||
}
|
||||
StaticFileSegment::TransactionSenders => {
|
||||
let sender =
|
||||
<<tables::TransactionSenders as Table>::Value>::decompress(
|
||||
content[0].as_slice(),
|
||||
)?;
|
||||
println!("{}", serde_json::to_string_pretty(&sender)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ use alloy_primitives::hex;
|
||||
use clap::Parser;
|
||||
use eyre::WrapErr;
|
||||
use reth_chainspec::EthereumHardforks;
|
||||
use reth_db::{transaction::DbTx, DatabaseEnv};
|
||||
use reth_db::DatabaseEnv;
|
||||
use reth_db_api::{database::Database, table::Table, RawValue, TableViewer, Tables};
|
||||
use reth_db_common::{DbTool, ListFilter};
|
||||
use reth_node_builder::{NodeTypes, NodeTypesWithDBAdapter};
|
||||
@@ -96,9 +96,6 @@ impl<N: NodeTypes> TableViewer<()> for ListTableViewer<'_, N> {
|
||||
|
||||
fn view<T: Table>(&self) -> Result<(), Self::Error> {
|
||||
self.tool.provider_factory.db_ref().view(|tx| {
|
||||
// We may be using the tui for a long time
|
||||
tx.disable_long_read_transaction_safety();
|
||||
|
||||
let table_db = tx.inner.open_db(Some(self.args.table.name())).wrap_err("Could not open db.")?;
|
||||
let stats = tx.inner.db_stat(&table_db).wrap_err(format!("Could not find table: {}", self.args.table.name()))?;
|
||||
let total_entries = stats.entries();
|
||||
|
||||
@@ -8,15 +8,12 @@ use std::{
|
||||
io::{self, Write},
|
||||
sync::Arc,
|
||||
};
|
||||
mod account_storage;
|
||||
mod checksum;
|
||||
mod clear;
|
||||
mod diff;
|
||||
mod get;
|
||||
mod list;
|
||||
mod repair_trie;
|
||||
mod settings;
|
||||
mod static_file_header;
|
||||
mod stats;
|
||||
/// DB List TUI
|
||||
mod tui;
|
||||
@@ -54,23 +51,16 @@ pub enum Subcommands {
|
||||
Clear(clear::Command),
|
||||
/// Verifies trie consistency and outputs any inconsistencies
|
||||
RepairTrie(repair_trie::Command),
|
||||
/// Reads and displays the static file segment header
|
||||
StaticFileHeader(static_file_header::Command),
|
||||
/// Lists current and local database versions
|
||||
Version,
|
||||
/// Returns the full database path
|
||||
Path,
|
||||
/// Manage storage settings
|
||||
Settings(settings::Command),
|
||||
/// Gets storage size information for an account
|
||||
AccountStorage(account_storage::Command),
|
||||
}
|
||||
|
||||
/// Initializes a provider factory with specified access rights, and then execute with the provided
|
||||
/// command
|
||||
macro_rules! db_exec {
|
||||
($env:expr, $tool:ident, $N:ident, $access_rights:expr, $command:block) => {
|
||||
let Environment { provider_factory, .. } = $env.init::<$N>($access_rights)?;
|
||||
/// `db_ro_exec` opens a database in read-only mode, and then execute with the provided command
|
||||
macro_rules! db_ro_exec {
|
||||
($env:expr, $tool:ident, $N:ident, $command:block) => {
|
||||
let Environment { provider_factory, .. } = $env.init::<$N>(AccessRights::RO)?;
|
||||
|
||||
let $tool = DbTool::new(provider_factory)?;
|
||||
$command;
|
||||
@@ -98,32 +88,27 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C>
|
||||
match self.command {
|
||||
// TODO: We'll need to add this on the DB trait.
|
||||
Subcommands::Stats(command) => {
|
||||
let access_rights = if command.skip_consistency_checks {
|
||||
AccessRights::RoInconsistent
|
||||
} else {
|
||||
AccessRights::RO
|
||||
};
|
||||
db_exec!(self.env, tool, N, access_rights, {
|
||||
db_ro_exec!(self.env, tool, N, {
|
||||
command.execute(data_dir, &tool)?;
|
||||
});
|
||||
}
|
||||
Subcommands::List(command) => {
|
||||
db_exec!(self.env, tool, N, AccessRights::RO, {
|
||||
db_ro_exec!(self.env, tool, N, {
|
||||
command.execute(&tool)?;
|
||||
});
|
||||
}
|
||||
Subcommands::Checksum(command) => {
|
||||
db_exec!(self.env, tool, N, AccessRights::RO, {
|
||||
db_ro_exec!(self.env, tool, N, {
|
||||
command.execute(&tool)?;
|
||||
});
|
||||
}
|
||||
Subcommands::Diff(command) => {
|
||||
db_exec!(self.env, tool, N, AccessRights::RO, {
|
||||
db_ro_exec!(self.env, tool, N, {
|
||||
command.execute(&tool)?;
|
||||
});
|
||||
}
|
||||
Subcommands::Get(command) => {
|
||||
db_exec!(self.env, tool, N, AccessRights::RO, {
|
||||
db_ro_exec!(self.env, tool, N, {
|
||||
command.execute(&tool)?;
|
||||
});
|
||||
}
|
||||
@@ -145,26 +130,19 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C>
|
||||
}
|
||||
}
|
||||
|
||||
db_exec!(self.env, tool, N, AccessRights::RW, {
|
||||
tool.drop(db_path, static_files_path, exex_wal_path)?;
|
||||
});
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
|
||||
let tool = DbTool::new(provider_factory)?;
|
||||
tool.drop(db_path, static_files_path, exex_wal_path)?;
|
||||
}
|
||||
Subcommands::Clear(command) => {
|
||||
db_exec!(self.env, tool, N, AccessRights::RW, {
|
||||
command.execute(&tool)?;
|
||||
});
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RW)?;
|
||||
command.execute(provider_factory)?;
|
||||
}
|
||||
Subcommands::RepairTrie(command) => {
|
||||
let access_rights =
|
||||
if command.dry_run { AccessRights::RO } else { AccessRights::RW };
|
||||
db_exec!(self.env, tool, N, access_rights, {
|
||||
command.execute(&tool)?;
|
||||
});
|
||||
}
|
||||
Subcommands::StaticFileHeader(command) => {
|
||||
db_exec!(self.env, tool, N, AccessRights::RoInconsistent, {
|
||||
command.execute(&tool)?;
|
||||
});
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(access_rights)?;
|
||||
command.execute(provider_factory)?;
|
||||
}
|
||||
Subcommands::Version => {
|
||||
let local_db_version = match get_db_version(&db_path) {
|
||||
@@ -184,16 +162,6 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> Command<C>
|
||||
Subcommands::Path => {
|
||||
println!("{}", db_path.display());
|
||||
}
|
||||
Subcommands::Settings(command) => {
|
||||
db_exec!(self.env, tool, N, command.access_rights(), {
|
||||
command.execute(&tool)?;
|
||||
});
|
||||
}
|
||||
Subcommands::AccountStorage(command) => {
|
||||
db_exec!(self.env, tool, N, AccessRights::RO, {
|
||||
command.execute(&tool)?;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -1,22 +1,12 @@
|
||||
use clap::Parser;
|
||||
use metrics::{self, Counter};
|
||||
use reth_chainspec::EthChainSpec;
|
||||
use reth_cli_util::parse_socket_address;
|
||||
use reth_db_api::{
|
||||
cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO},
|
||||
database::Database,
|
||||
tables,
|
||||
transaction::{DbTx, DbTxMut},
|
||||
};
|
||||
use reth_db_common::DbTool;
|
||||
use reth_node_core::version::version_metadata;
|
||||
use reth_node_metrics::{
|
||||
chain::ChainSpecInfo,
|
||||
hooks::Hooks,
|
||||
server::{MetricServer, MetricServerConfig},
|
||||
version::VersionInfo,
|
||||
};
|
||||
use reth_provider::{providers::ProviderNodeTypes, ChainSpecProvider, StageCheckpointReader};
|
||||
use reth_node_builder::NodeTypesWithDB;
|
||||
use reth_provider::{providers::ProviderNodeTypes, ProviderFactory, StageCheckpointReader};
|
||||
use reth_stages::StageId;
|
||||
use reth_trie::{
|
||||
verify::{Output, Verifier},
|
||||
@@ -24,10 +14,7 @@ use reth_trie::{
|
||||
};
|
||||
use reth_trie_common::{StorageTrieEntry, StoredNibbles, StoredNibblesSubKey};
|
||||
use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
|
||||
use std::{
|
||||
net::SocketAddr,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use std::time::{Duration, Instant};
|
||||
use tracing::{info, warn};
|
||||
|
||||
const PROGRESS_PERIOD: Duration = Duration::from_secs(5);
|
||||
@@ -38,89 +25,27 @@ pub struct Command {
|
||||
/// Only show inconsistencies without making any repairs
|
||||
#[arg(long)]
|
||||
pub(crate) dry_run: bool,
|
||||
|
||||
/// Enable Prometheus metrics.
|
||||
///
|
||||
/// The metrics will be served at the given interface and port.
|
||||
#[arg(long = "metrics", value_name = "ADDR:PORT", value_parser = parse_socket_address)]
|
||||
pub(crate) metrics: Option<SocketAddr>,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Execute `db repair-trie` command
|
||||
pub fn execute<N: ProviderNodeTypes>(self, 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 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");
|
||||
|
||||
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 {
|
||||
None
|
||||
};
|
||||
|
||||
pub fn execute<N: ProviderNodeTypes>(
|
||||
self,
|
||||
provider_factory: ProviderFactory<N>,
|
||||
) -> eyre::Result<()> {
|
||||
if self.dry_run {
|
||||
verify_only(tool)?
|
||||
verify_only(provider_factory)?
|
||||
} else {
|
||||
verify_and_repair(tool)?
|
||||
verify_and_repair(provider_factory)?
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn verify_only<N: ProviderNodeTypes>(tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
// Log the database block tip from Finish stage checkpoint
|
||||
let finish_checkpoint = tool
|
||||
.provider_factory
|
||||
.provider()?
|
||||
.get_stage_checkpoint(StageId::Finish)?
|
||||
.unwrap_or_default();
|
||||
info!("Database block tip: {}", finish_checkpoint.block_number);
|
||||
|
||||
fn verify_only<N: NodeTypesWithDB>(provider_factory: ProviderFactory<N>) -> eyre::Result<()> {
|
||||
// Get a database transaction directly from the database
|
||||
let db = tool.provider_factory.db_ref();
|
||||
let db = provider_factory.db_ref();
|
||||
let mut tx = db.tx()?;
|
||||
tx.disable_long_read_transaction_safety();
|
||||
|
||||
@@ -129,8 +54,6 @@ fn verify_only<N: ProviderNodeTypes>(tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
let trie_cursor_factory = DatabaseTrieCursorFactory::new(&tx);
|
||||
let verifier = Verifier::new(&trie_cursor_factory, hashed_cursor_factory)?;
|
||||
|
||||
let metrics = RepairTrieMetrics::new();
|
||||
|
||||
let mut inconsistent_nodes = 0;
|
||||
let start_time = Instant::now();
|
||||
let mut last_progress_time = Instant::now();
|
||||
@@ -147,21 +70,6 @@ fn verify_only<N: ProviderNodeTypes>(tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
} else {
|
||||
warn!("Inconsistency found: {output:?}");
|
||||
inconsistent_nodes += 1;
|
||||
|
||||
// Record metrics based on output type
|
||||
match output {
|
||||
Output::AccountExtra(_, _) |
|
||||
Output::AccountWrong { .. } |
|
||||
Output::AccountMissing(_, _) => {
|
||||
metrics.account_inconsistencies.increment(1);
|
||||
}
|
||||
Output::StorageExtra(_, _, _) |
|
||||
Output::StorageWrong { .. } |
|
||||
Output::StorageMissing(_, _, _) => {
|
||||
metrics.storage_inconsistencies.increment(1);
|
||||
}
|
||||
Output::Progress(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,13 +114,11 @@ fn verify_checkpoints(provider: impl StageCheckpointReader) -> eyre::Result<()>
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_and_repair<N: ProviderNodeTypes>(tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
fn verify_and_repair<N: ProviderNodeTypes>(
|
||||
provider_factory: ProviderFactory<N>,
|
||||
) -> eyre::Result<()> {
|
||||
// Get a read-write database provider
|
||||
let mut provider_rw = tool.provider_factory.provider_rw()?;
|
||||
|
||||
// Log the database block tip from Finish stage checkpoint
|
||||
let finish_checkpoint = provider_rw.get_stage_checkpoint(StageId::Finish)?.unwrap_or_default();
|
||||
info!("Database block tip: {}", finish_checkpoint.block_number);
|
||||
let mut provider_rw = provider_factory.provider_rw()?;
|
||||
|
||||
// Check that a pipeline sync isn't in progress.
|
||||
verify_checkpoints(provider_rw.as_ref())?;
|
||||
@@ -232,8 +138,6 @@ fn verify_and_repair<N: ProviderNodeTypes>(tool: &DbTool<N>) -> eyre::Result<()>
|
||||
// Create the verifier
|
||||
let verifier = Verifier::new(&trie_cursor_factory, hashed_cursor_factory)?;
|
||||
|
||||
let metrics = RepairTrieMetrics::new();
|
||||
|
||||
let mut inconsistent_nodes = 0;
|
||||
let start_time = Instant::now();
|
||||
let mut last_progress_time = Instant::now();
|
||||
@@ -245,21 +149,6 @@ fn verify_and_repair<N: ProviderNodeTypes>(tool: &DbTool<N>) -> eyre::Result<()>
|
||||
if !matches!(output, Output::Progress(_)) {
|
||||
warn!("Inconsistency found, will repair: {output:?}");
|
||||
inconsistent_nodes += 1;
|
||||
|
||||
// Record metrics based on output type
|
||||
match &output {
|
||||
Output::AccountExtra(_, _) |
|
||||
Output::AccountWrong { .. } |
|
||||
Output::AccountMissing(_, _) => {
|
||||
metrics.account_inconsistencies.increment(1);
|
||||
}
|
||||
Output::StorageExtra(_, _, _) |
|
||||
Output::StorageWrong { .. } |
|
||||
Output::StorageMissing(_, _, _) => {
|
||||
metrics.storage_inconsistencies.increment(1);
|
||||
}
|
||||
Output::Progress(_) => {}
|
||||
}
|
||||
}
|
||||
|
||||
match output {
|
||||
@@ -358,25 +247,3 @@ fn output_progress(last_account: Nibbles, start_time: Instant, inconsistent_node
|
||||
"Repairing trie tables",
|
||||
);
|
||||
}
|
||||
|
||||
/// Metrics for tracking trie repair inconsistencies
|
||||
#[derive(Debug)]
|
||||
struct RepairTrieMetrics {
|
||||
account_inconsistencies: Counter,
|
||||
storage_inconsistencies: Counter,
|
||||
}
|
||||
|
||||
impl RepairTrieMetrics {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
account_inconsistencies: metrics::counter!(
|
||||
"db.repair_trie.inconsistencies_found",
|
||||
"type" => "account"
|
||||
),
|
||||
storage_inconsistencies: metrics::counter!(
|
||||
"db.repair_trie.inconsistencies_found",
|
||||
"type" => "storage"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
//! `reth db settings` command for managing storage settings
|
||||
|
||||
use clap::{ArgAction, Parser, Subcommand};
|
||||
use reth_db_common::DbTool;
|
||||
use reth_provider::{
|
||||
providers::ProviderNodeTypes, DBProvider, DatabaseProviderFactory, MetadataProvider,
|
||||
MetadataWriter, StorageSettings,
|
||||
};
|
||||
|
||||
use crate::common::AccessRights;
|
||||
|
||||
/// `reth db settings` subcommand
|
||||
#[derive(Debug, Parser)]
|
||||
pub struct Command {
|
||||
#[command(subcommand)]
|
||||
command: Subcommands,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Returns database access rights required for the command.
|
||||
pub fn access_rights(&self) -> AccessRights {
|
||||
match self.command {
|
||||
Subcommands::Get => AccessRights::RO,
|
||||
Subcommands::Set(_) => AccessRights::RW,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Subcommand)]
|
||||
enum Subcommands {
|
||||
/// Get current storage settings from database
|
||||
Get,
|
||||
/// Set storage settings in database
|
||||
#[clap(subcommand)]
|
||||
Set(SetCommand),
|
||||
}
|
||||
|
||||
/// Set storage settings
|
||||
#[derive(Debug, Clone, Copy, Subcommand)]
|
||||
#[clap(rename_all = "snake_case")]
|
||||
pub enum SetCommand {
|
||||
/// Store receipts in static files instead of the database
|
||||
ReceiptsInStaticFiles {
|
||||
#[clap(action(ArgAction::Set))]
|
||||
value: bool,
|
||||
},
|
||||
/// Store transaction senders in static files instead of the database
|
||||
TransactionSendersInStaticFiles {
|
||||
#[clap(action(ArgAction::Set))]
|
||||
value: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Execute the command
|
||||
pub fn execute<N: ProviderNodeTypes>(self, tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
match self.command {
|
||||
Subcommands::Get => self.get(tool),
|
||||
Subcommands::Set(cmd) => self.set(cmd, tool),
|
||||
}
|
||||
}
|
||||
|
||||
fn get<N: ProviderNodeTypes>(&self, tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
// Read storage settings
|
||||
let provider = tool.provider_factory.provider()?;
|
||||
let storage_settings = provider.storage_settings()?;
|
||||
|
||||
// Display settings
|
||||
match storage_settings {
|
||||
Some(settings) => {
|
||||
println!("Current storage settings:");
|
||||
println!("{settings:#?}");
|
||||
}
|
||||
None => {
|
||||
println!("No storage settings found.");
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set<N: ProviderNodeTypes>(&self, cmd: SetCommand, tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
// Read storage settings
|
||||
let provider_rw = tool.provider_factory.database_provider_rw()?;
|
||||
// Destruct settings struct to not miss adding support for new fields
|
||||
let settings = provider_rw.storage_settings()?;
|
||||
if settings.is_none() {
|
||||
println!("No storage settings found, creating new settings.");
|
||||
}
|
||||
|
||||
let mut settings @ StorageSettings {
|
||||
receipts_in_static_files: _,
|
||||
transaction_senders_in_static_files: _,
|
||||
storages_history_in_rocksdb: _,
|
||||
} = settings.unwrap_or_else(StorageSettings::legacy);
|
||||
|
||||
// Update the setting based on the key
|
||||
match cmd {
|
||||
SetCommand::ReceiptsInStaticFiles { value } => {
|
||||
if settings.receipts_in_static_files == value {
|
||||
println!("receipts_in_static_files is already set to {}", value);
|
||||
return Ok(());
|
||||
}
|
||||
settings.receipts_in_static_files = value;
|
||||
println!("Set receipts_in_static_files = {}", value);
|
||||
}
|
||||
SetCommand::TransactionSendersInStaticFiles { value } => {
|
||||
if settings.transaction_senders_in_static_files == value {
|
||||
println!("transaction_senders_in_static_files is already set to {}", value);
|
||||
return Ok(());
|
||||
}
|
||||
settings.transaction_senders_in_static_files = value;
|
||||
println!("Set transaction_senders_in_static_files = {}", value);
|
||||
}
|
||||
}
|
||||
|
||||
// Write updated settings
|
||||
provider_rw.write_storage_settings(settings)?;
|
||||
provider_rw.commit()?;
|
||||
|
||||
println!("Storage settings updated successfully.");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
use clap::{Parser, Subcommand};
|
||||
use reth_db_common::DbTool;
|
||||
use reth_provider::{providers::ProviderNodeTypes, StaticFileProviderFactory};
|
||||
use reth_static_file_types::StaticFileSegment;
|
||||
use std::path::PathBuf;
|
||||
use tracing::warn;
|
||||
|
||||
/// The arguments for the `reth db static-file-header` command
|
||||
#[derive(Parser, Debug)]
|
||||
pub struct Command {
|
||||
#[command(subcommand)]
|
||||
source: Source,
|
||||
}
|
||||
|
||||
/// Source for locating the static file
|
||||
#[derive(Subcommand, Debug)]
|
||||
enum Source {
|
||||
/// Query by segment and block number
|
||||
Block {
|
||||
/// Static file segment
|
||||
#[arg(value_enum)]
|
||||
segment: StaticFileSegment,
|
||||
/// Block number to query
|
||||
block: u64,
|
||||
},
|
||||
/// Query by path to static file
|
||||
Path {
|
||||
/// Path to the static file
|
||||
path: PathBuf,
|
||||
},
|
||||
}
|
||||
|
||||
impl Command {
|
||||
/// Execute `db static-file-header` command
|
||||
pub fn execute<N: ProviderNodeTypes>(self, tool: &DbTool<N>) -> eyre::Result<()> {
|
||||
let static_file_provider = tool.provider_factory.static_file_provider();
|
||||
if let Err(err) = static_file_provider.check_consistency(&tool.provider_factory.provider()?)
|
||||
{
|
||||
warn!("Error checking consistency of static files: {err}");
|
||||
}
|
||||
|
||||
// Get the provider based on the source
|
||||
let provider = match self.source {
|
||||
Source::Path { path } => {
|
||||
static_file_provider.get_segment_provider_for_path(&path)?.ok_or_else(|| {
|
||||
eyre::eyre!("Could not find static file segment for path: {}", path.display())
|
||||
})?
|
||||
}
|
||||
Source::Block { segment, block } => {
|
||||
static_file_provider.get_segment_provider(segment, block)?
|
||||
}
|
||||
};
|
||||
|
||||
let header = provider.user_header();
|
||||
|
||||
println!("Segment: {}", header.segment());
|
||||
println!("Expected Block Range: {}", header.expected_block_range());
|
||||
println!("Block Range: {:?}", header.block_range());
|
||||
println!("Transaction Range: {:?}", header.tx_range());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -18,10 +18,6 @@ use std::{sync::Arc, time::Duration};
|
||||
#[derive(Parser, Debug)]
|
||||
/// The arguments for the `reth db stats` command
|
||||
pub struct Command {
|
||||
/// Skip consistency checks for static files.
|
||||
#[arg(long, default_value_t = false)]
|
||||
pub(crate) skip_consistency_checks: bool,
|
||||
|
||||
/// Show only the total size for static files.
|
||||
#[arg(long, default_value_t = false)]
|
||||
detailed_sizes: bool,
|
||||
@@ -195,11 +191,10 @@ impl Command {
|
||||
mut segment_config_size,
|
||||
) = (0, 0, 0, 0, 0, 0);
|
||||
|
||||
for (block_range, header) in &ranges {
|
||||
let fixed_block_range =
|
||||
static_file_provider.find_fixed_range(segment, block_range.start());
|
||||
for (block_range, tx_range) in &ranges {
|
||||
let fixed_block_range = static_file_provider.find_fixed_range(block_range.start());
|
||||
let jar_provider = static_file_provider
|
||||
.get_segment_provider_for_range(segment, || Some(fixed_block_range), None)?
|
||||
.get_segment_provider(segment, || Some(fixed_block_range), None)?
|
||||
.ok_or_else(|| {
|
||||
eyre::eyre!("Failed to get segment provider for segment: {}", segment)
|
||||
})?;
|
||||
@@ -225,7 +220,7 @@ impl Command {
|
||||
row.add_cell(Cell::new(segment))
|
||||
.add_cell(Cell::new(format!("{block_range}")))
|
||||
.add_cell(Cell::new(
|
||||
header.tx_range().map_or("N/A".to_string(), |range| format!("{range}")),
|
||||
tx_range.map_or("N/A".to_string(), |tx_range| format!("{tx_range}")),
|
||||
))
|
||||
.add_cell(Cell::new(format!("{columns} x {rows}")));
|
||||
if self.detailed_sizes {
|
||||
@@ -275,12 +270,10 @@ impl Command {
|
||||
let tx_range = {
|
||||
let start = ranges
|
||||
.iter()
|
||||
.find_map(|(_, header)| header.tx_range().map(|range| range.start()))
|
||||
.find_map(|(_, tx_range)| tx_range.map(|r| r.start()))
|
||||
.unwrap_or_default();
|
||||
let end = ranges
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|(_, header)| header.tx_range().map(|range| range.end()));
|
||||
let end =
|
||||
ranges.iter().rev().find_map(|(_, tx_range)| tx_range.map(|r| r.end()));
|
||||
end.map(|end| SegmentRangeInclusive::new(start, end))
|
||||
};
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use clap::{Args, Parser};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_era::era1::types::execution::MAX_BLOCKS_PER_ERA1;
|
||||
use reth_era::execution_types::MAX_BLOCKS_PER_ERA1;
|
||||
use reth_era_utils as era1;
|
||||
use reth_provider::DatabaseProviderFactory;
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Command that initializes the node from a genesis file.
|
||||
|
||||
use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use crate::common::{AccessRights, CliHeader, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use alloy_consensus::BlockHeader as AlloyBlockHeader;
|
||||
use alloy_primitives::{Sealable, B256};
|
||||
use clap::Parser;
|
||||
@@ -8,7 +8,7 @@ use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_db_common::init::init_from_state_dump;
|
||||
use reth_node_api::NodePrimitives;
|
||||
use reth_primitives_traits::{header::HeaderMut, SealedHeader};
|
||||
use reth_primitives_traits::{BlockHeader, SealedHeader};
|
||||
use reth_provider::{
|
||||
BlockNumReader, DBProvider, DatabaseProviderFactory, StaticFileProviderFactory,
|
||||
StaticFileWriter,
|
||||
@@ -69,7 +69,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + EthereumHardforks>> InitStateC
|
||||
where
|
||||
N: CliNodeTypes<
|
||||
ChainSpec = C::ChainSpec,
|
||||
Primitives: NodePrimitives<BlockHeader: HeaderMut>,
|
||||
Primitives: NodePrimitives<BlockHeader: BlockHeader + CliHeader>,
|
||||
>,
|
||||
{
|
||||
info!(target: "reth::cli", "Reth init-state starting");
|
||||
|
||||
@@ -10,7 +10,7 @@ use reth_node_builder::NodeBuilder;
|
||||
use reth_node_core::{
|
||||
args::{
|
||||
DatabaseArgs, DatadirArgs, DebugArgs, DevArgs, EngineArgs, EraArgs, MetricArgs,
|
||||
NetworkArgs, PayloadBuilderArgs, PruningArgs, RpcServerArgs, StaticFilesArgs, TxPoolArgs,
|
||||
NetworkArgs, PayloadBuilderArgs, PruningArgs, RpcServerArgs, TxPoolArgs,
|
||||
},
|
||||
node_config::NodeConfig,
|
||||
version,
|
||||
@@ -110,10 +110,6 @@ pub struct NodeCommand<C: ChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs
|
||||
#[command(flatten, next_help_heading = "ERA")]
|
||||
pub era: EraArgs,
|
||||
|
||||
/// All static files related arguments
|
||||
#[command(flatten, next_help_heading = "Static Files")]
|
||||
pub static_files: StaticFilesArgs,
|
||||
|
||||
/// Additional cli arguments
|
||||
#[command(flatten, next_help_heading = "Extension")]
|
||||
pub ext: Ext,
|
||||
@@ -149,7 +145,7 @@ where
|
||||
where
|
||||
L: Launcher<C, Ext>,
|
||||
{
|
||||
tracing::info!(target: "reth::cli", version = ?version::version_metadata().short_version, "Starting {}", version::version_metadata().name_client);
|
||||
tracing::info!(target: "reth::cli", version = ?version::version_metadata().short_version, "Starting reth");
|
||||
|
||||
let Self {
|
||||
datadir,
|
||||
@@ -166,10 +162,9 @@ where
|
||||
db,
|
||||
dev,
|
||||
pruning,
|
||||
ext,
|
||||
engine,
|
||||
era,
|
||||
static_files,
|
||||
ext,
|
||||
} = self;
|
||||
|
||||
// set up node config
|
||||
@@ -189,7 +184,6 @@ where
|
||||
pruning,
|
||||
engine,
|
||||
era,
|
||||
static_files,
|
||||
};
|
||||
|
||||
let data_dir = node_config.datadir();
|
||||
|
||||
@@ -60,7 +60,7 @@ impl Command {
|
||||
if self.v5 {
|
||||
info!("Starting discv5");
|
||||
let config = Config::builder(self.addr).build();
|
||||
let (_discv5, updates) = Discv5::start(&sk, config).await?;
|
||||
let (_discv5, updates, _local_enr_discv5) = Discv5::start(&sk, config).await?;
|
||||
discv5_updates = Some(updates);
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ use backon::{ConstantBuilder, Retryable};
|
||||
use clap::{Parser, Subcommand};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_util::hash_or_num_value_parser;
|
||||
use reth_cli_util::{get_secret_key, hash_or_num_value_parser};
|
||||
use reth_config::Config;
|
||||
use reth_network::{BlockDownloaderProvider, NetworkConfigBuilder};
|
||||
use reth_network_p2p::bodies::client::BodiesClient;
|
||||
@@ -183,7 +183,9 @@ impl<C: ChainSpecParser> DownloadArgs<C> {
|
||||
config.peers.trusted_nodes_only = self.network.trusted_only;
|
||||
|
||||
let default_secret_key_path = data_dir.p2p_secret();
|
||||
let p2p_secret_key = self.network.secret_key(default_secret_key_path)?;
|
||||
let secret_key_path =
|
||||
self.network.p2p_secret_key.clone().unwrap_or(default_secret_key_path);
|
||||
let p2p_secret_key = get_secret_key(&secret_key_path)?;
|
||||
let rlpx_socket = (self.network.addr, self.network.port).into();
|
||||
let boot_nodes = self.chain.bootnodes().unwrap_or_default();
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use clap::Parser;
|
||||
use eyre::WrapErr;
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks, Hardforks};
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_cli_util::cancellation::CancellationToken;
|
||||
use reth_consensus::FullConsensus;
|
||||
use reth_evm::{execute::Executor, ConfigureEvm};
|
||||
use reth_primitives_traits::{format_gas_throughput, BlockBody, GotExpected};
|
||||
@@ -45,10 +44,6 @@ pub struct Command<C: ChainSpecParser> {
|
||||
/// Number of tasks to run in parallel
|
||||
#[arg(long, default_value = "10")]
|
||||
num_tasks: u64,
|
||||
|
||||
/// Continues with execution when an invalid block is encountered and collects these blocks.
|
||||
#[arg(long)]
|
||||
skip_invalid_blocks: bool,
|
||||
}
|
||||
|
||||
impl<C: ChainSpecParser> Command<C> {
|
||||
@@ -66,23 +61,11 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
{
|
||||
let Environment { provider_factory, .. } = self.env.init::<N>(AccessRights::RO)?;
|
||||
|
||||
let provider = provider_factory.database_provider_ro()?;
|
||||
let components = components(provider_factory.chain_spec());
|
||||
|
||||
let min_block = self.from;
|
||||
let best_block = DatabaseProviderFactory::database_provider_ro(&provider_factory)?
|
||||
.best_block_number()?;
|
||||
let mut max_block = best_block;
|
||||
if let Some(to) = self.to {
|
||||
if to > best_block {
|
||||
warn!(
|
||||
requested = to,
|
||||
best_block,
|
||||
"Requested --to is beyond available chain head; clamping to best block"
|
||||
);
|
||||
} else {
|
||||
max_block = to;
|
||||
}
|
||||
};
|
||||
let max_block = self.to.unwrap_or(provider.best_block_number()?);
|
||||
|
||||
let total_blocks = max_block - min_block;
|
||||
let total_gas = calculate_gas_used_from_headers(
|
||||
@@ -100,11 +83,7 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
}
|
||||
};
|
||||
|
||||
let skip_invalid_blocks = self.skip_invalid_blocks;
|
||||
let (stats_tx, mut stats_rx) = mpsc::unbounded_channel();
|
||||
let (info_tx, mut info_rx) = mpsc::unbounded_channel();
|
||||
let cancellation = CancellationToken::new();
|
||||
let _guard = cancellation.drop_guard();
|
||||
|
||||
let mut tasks = JoinSet::new();
|
||||
for i in 0..self.num_tasks {
|
||||
@@ -118,40 +97,17 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
let consensus = components.consensus().clone();
|
||||
let db_at = db_at.clone();
|
||||
let stats_tx = stats_tx.clone();
|
||||
let info_tx = info_tx.clone();
|
||||
let cancellation = cancellation.clone();
|
||||
tasks.spawn_blocking(move || {
|
||||
let mut executor = evm_config.batch_executor(db_at(start_block - 1));
|
||||
let mut executor_created = Instant::now();
|
||||
let executor_lifetime = Duration::from_secs(120);
|
||||
|
||||
'blocks: for block in start_block..end_block {
|
||||
if cancellation.is_cancelled() {
|
||||
// exit if the program is being terminated
|
||||
break
|
||||
}
|
||||
|
||||
for block in start_block..end_block {
|
||||
let block = provider_factory
|
||||
.recovered_block(block.into(), TransactionVariant::NoHash)?
|
||||
.unwrap();
|
||||
|
||||
let result = match executor.execute_one(&block) {
|
||||
Ok(result) => result,
|
||||
Err(err) => {
|
||||
if skip_invalid_blocks {
|
||||
executor = evm_config.batch_executor(db_at(block.number()));
|
||||
let _ = info_tx.send((block, eyre::Report::new(err)));
|
||||
continue
|
||||
}
|
||||
return Err(err.into())
|
||||
}
|
||||
};
|
||||
let result = executor.execute_one(&block)?;
|
||||
|
||||
if let Err(err) = consensus
|
||||
.validate_block_post_execution(&block, &result)
|
||||
.wrap_err_with(|| {
|
||||
format!("Failed to validate block {} {}", block.number(), block.hash())
|
||||
})
|
||||
.wrap_err_with(|| format!("Failed to validate block {}", block.number()))
|
||||
{
|
||||
let correct_receipts =
|
||||
provider_factory.receipts_by_block(block.number().into())?.unwrap();
|
||||
@@ -187,11 +143,6 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
};
|
||||
|
||||
error!(number=?block.number(), ?mismatch, "Gas usage mismatch");
|
||||
if skip_invalid_blocks {
|
||||
executor = evm_config.batch_executor(db_at(block.number()));
|
||||
let _ = info_tx.send((block, err));
|
||||
continue 'blocks;
|
||||
}
|
||||
return Err(err);
|
||||
}
|
||||
} else {
|
||||
@@ -203,12 +154,9 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
}
|
||||
let _ = stats_tx.send(block.gas_used());
|
||||
|
||||
// Reset DB once in a while to avoid OOM or read tx timeouts
|
||||
if executor.size_hint() > 1_000_000 ||
|
||||
executor_created.elapsed() > executor_lifetime
|
||||
{
|
||||
// Reset DB once in a while to avoid OOM
|
||||
if executor.size_hint() > 1_000_000 {
|
||||
executor = evm_config.batch_executor(db_at(block.number()));
|
||||
executor_created = Instant::now();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,7 +171,6 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
let mut last_logged_gas = 0;
|
||||
let mut last_logged_blocks = 0;
|
||||
let mut last_logged_time = Instant::now();
|
||||
let mut invalid_blocks = Vec::new();
|
||||
|
||||
let mut interval = tokio::time::interval(Duration::from_secs(10));
|
||||
|
||||
@@ -233,10 +180,6 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
total_executed_blocks += 1;
|
||||
total_executed_gas += gas_used;
|
||||
}
|
||||
Some((block, err)) = info_rx.recv() => {
|
||||
error!(?err, block=?block.num_hash(), "Invalid block");
|
||||
invalid_blocks.push(block.num_hash());
|
||||
}
|
||||
result = tasks.join_next() => {
|
||||
if let Some(result) = result {
|
||||
if matches!(result, Err(_) | Ok(Err(_))) {
|
||||
@@ -267,25 +210,12 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
}
|
||||
}
|
||||
|
||||
if invalid_blocks.is_empty() {
|
||||
info!(
|
||||
start_block = min_block,
|
||||
end_block = max_block,
|
||||
%total_executed_blocks,
|
||||
throughput=?format_gas_throughput(total_executed_gas, instant.elapsed()),
|
||||
"Re-executed successfully"
|
||||
);
|
||||
} else {
|
||||
info!(
|
||||
start_block = min_block,
|
||||
end_block = max_block,
|
||||
%total_executed_blocks,
|
||||
invalid_block_count = invalid_blocks.len(),
|
||||
?invalid_blocks,
|
||||
throughput=?format_gas_throughput(total_executed_gas, instant.elapsed()),
|
||||
"Re-executed with invalid blocks"
|
||||
);
|
||||
}
|
||||
info!(
|
||||
start_block = min_block,
|
||||
end_block = max_block,
|
||||
throughput=?format_gas_throughput(total_executed_gas, instant.elapsed()),
|
||||
"Re-executed successfully"
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
//! Database debugging tool
|
||||
use crate::common::{AccessRights, CliNodeTypes, Environment, EnvironmentArgs};
|
||||
use clap::Parser;
|
||||
use itertools::Itertools;
|
||||
use reth_chainspec::EthChainSpec;
|
||||
use reth_cli::chainspec::ChainSpecParser;
|
||||
use reth_db::{mdbx::tx::Tx, DatabaseError};
|
||||
use reth_db::{mdbx::tx::Tx, static_file::iter_static_files, DatabaseError};
|
||||
use reth_db_api::{
|
||||
tables,
|
||||
transaction::{DbTx, DbTxMut},
|
||||
@@ -14,9 +15,7 @@ use reth_db_common::{
|
||||
};
|
||||
use reth_node_api::{HeaderTy, ReceiptTy, TxTy};
|
||||
use reth_node_core::args::StageEnum;
|
||||
use reth_provider::{
|
||||
DBProvider, DatabaseProviderFactory, StaticFileProviderFactory, StaticFileWriter, TrieWriter,
|
||||
};
|
||||
use reth_provider::{DBProvider, DatabaseProviderFactory, StaticFileProviderFactory, TrieWriter};
|
||||
use reth_prune::PruneSegment;
|
||||
use reth_stages::StageId;
|
||||
use reth_static_file_types::StaticFileSegment;
|
||||
@@ -45,48 +44,21 @@ impl<C: ChainSpecParser> Command<C> {
|
||||
StageEnum::Headers => Some(StaticFileSegment::Headers),
|
||||
StageEnum::Bodies => Some(StaticFileSegment::Transactions),
|
||||
StageEnum::Execution => Some(StaticFileSegment::Receipts),
|
||||
StageEnum::Senders => Some(StaticFileSegment::TransactionSenders),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Calling `StaticFileProviderRW::prune_*` will instruct the writer to prune rows only
|
||||
// when `StaticFileProviderRW::commit` is called. We need to do that instead of
|
||||
// deleting the jar files, otherwise if the task were to be interrupted after we
|
||||
// have deleted them, BUT before we have committed the checkpoints to the database, we'd
|
||||
// lose essential data.
|
||||
// Delete static file segment data before inserting the genesis header below
|
||||
if let Some(static_file_segment) = static_file_segment {
|
||||
let static_file_provider = tool.provider_factory.static_file_provider();
|
||||
if let Some(highest_block) =
|
||||
static_file_provider.get_highest_static_file_block(static_file_segment)
|
||||
{
|
||||
let mut writer = static_file_provider.latest_writer(static_file_segment)?;
|
||||
|
||||
match static_file_segment {
|
||||
StaticFileSegment::Headers => {
|
||||
// Prune all headers leaving genesis intact.
|
||||
writer.prune_headers(highest_block)?;
|
||||
}
|
||||
StaticFileSegment::Transactions => {
|
||||
let to_delete = static_file_provider
|
||||
.get_highest_static_file_tx(static_file_segment)
|
||||
.map(|tx_num| tx_num + 1)
|
||||
.unwrap_or_default();
|
||||
writer.prune_transactions(to_delete, 0)?;
|
||||
}
|
||||
StaticFileSegment::Receipts => {
|
||||
let to_delete = static_file_provider
|
||||
.get_highest_static_file_tx(static_file_segment)
|
||||
.map(|tx_num| tx_num + 1)
|
||||
.unwrap_or_default();
|
||||
writer.prune_receipts(to_delete, 0)?;
|
||||
}
|
||||
StaticFileSegment::TransactionSenders => {
|
||||
let to_delete = static_file_provider
|
||||
.get_highest_static_file_tx(static_file_segment)
|
||||
.map(|tx_num| tx_num + 1)
|
||||
.unwrap_or_default();
|
||||
writer.prune_transaction_senders(to_delete, 0)?;
|
||||
}
|
||||
let static_files = iter_static_files(static_file_provider.directory())?;
|
||||
if let Some(segment_static_files) = static_files.get(&static_file_segment) {
|
||||
// Delete static files from the highest to the lowest block range
|
||||
for (block_range, _) in segment_static_files
|
||||
.iter()
|
||||
.sorted_by_key(|(block_range, _)| block_range.start())
|
||||
.rev()
|
||||
{
|
||||
static_file_provider.delete_jar(static_file_segment, block_range.start())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ where
|
||||
Arc::new(output_db),
|
||||
db_tool.chain(),
|
||||
StaticFileProvider::read_write(output_datadir.static_files())?,
|
||||
)?,
|
||||
),
|
||||
to,
|
||||
from,
|
||||
evm_config,
|
||||
|
||||
@@ -39,7 +39,7 @@ 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())?,
|
||||
)?,
|
||||
),
|
||||
to,
|
||||
from,
|
||||
)?;
|
||||
|
||||
@@ -29,7 +29,7 @@ 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())?,
|
||||
)?,
|
||||
),
|
||||
to,
|
||||
from,
|
||||
)?;
|
||||
|
||||
@@ -62,7 +62,7 @@ where
|
||||
Arc::new(output_db),
|
||||
db_tool.chain(),
|
||||
StaticFileProvider::read_write(output_datadir.static_files())?,
|
||||
)?,
|
||||
),
|
||||
to,
|
||||
from,
|
||||
)?;
|
||||
|
||||
@@ -84,9 +84,6 @@ pub struct Command<C: ChainSpecParser> {
|
||||
/// Commits the changes in the database. WARNING: potentially destructive.
|
||||
///
|
||||
/// Useful when you want to run diagnostics on the database.
|
||||
///
|
||||
/// NOTE: This flag is currently required for the headers, bodies, and execution stages because
|
||||
/// they use static files and must commit to properly unwind and run.
|
||||
// TODO: We should consider allowing to run hooks at the end of the stage run,
|
||||
// e.g. query the DB size, or any table data.
|
||||
#[arg(long, short)]
|
||||
@@ -108,14 +105,6 @@ impl<C: ChainSpecParser<ChainSpec: EthChainSpec + Hardforks + EthereumHardforks>
|
||||
Comp: CliNodeComponents<N>,
|
||||
F: FnOnce(Arc<C::ChainSpec>) -> Comp,
|
||||
{
|
||||
// Quit early if the stages requires a commit and `--commit` is not provided.
|
||||
if self.requires_commit() && !self.commit {
|
||||
return Err(eyre::eyre!(
|
||||
"The stage {} requires overwriting existing static files and must commit, but `--commit` was not provided. Please pass `--commit` and try again.",
|
||||
self.stage.to_string()
|
||||
));
|
||||
}
|
||||
|
||||
// Raise the fd limit of the process.
|
||||
// Does not do anything on windows.
|
||||
let _ = fdlimit::raise_fd_limit();
|
||||
@@ -394,13 +383,4 @@ impl<C: ChainSpecParser> Command<C> {
|
||||
pub fn chain_spec(&self) -> Option<&Arc<C::ChainSpec>> {
|
||||
Some(&self.env.chain)
|
||||
}
|
||||
|
||||
/// Returns whether or not the configured stage requires committing.
|
||||
///
|
||||
/// This is the case for stages that mainly modify static files, as there is no way to unwind
|
||||
/// these stages without committing anyways. This is because static files do not have
|
||||
/// transactions and we cannot change the view of headers without writing.
|
||||
pub fn requires_commit(&self) -> bool {
|
||||
matches!(self.stage, StageEnum::Headers | StageEnum::Bodies | StageEnum::Execution)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,9 +42,6 @@ jemalloc = ["dep:tikv-jemallocator"]
|
||||
# Enables jemalloc profiling features
|
||||
jemalloc-prof = ["jemalloc", "tikv-jemallocator?/profiling"]
|
||||
|
||||
# Enables unprefixed malloc (reproducible builds support)
|
||||
jemalloc-unprefixed = ["jemalloc", "tikv-jemallocator?/unprefixed_malloc_on_supported_platforms"]
|
||||
|
||||
# Wraps the selected allocator in the tracy profiling allocator
|
||||
tracy-allocator = ["dep:tracy-client"]
|
||||
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
//! Thread-safe cancellation primitives for cooperative task cancellation.
|
||||
|
||||
use std::sync::{
|
||||
atomic::{AtomicBool, Ordering},
|
||||
Arc,
|
||||
};
|
||||
|
||||
/// A thread-safe cancellation token that can be shared across threads.
|
||||
///
|
||||
/// This token allows cooperative cancellation by providing a way to signal
|
||||
/// cancellation and check cancellation status. The token can be cloned and
|
||||
/// shared across multiple threads, with all clones sharing the same cancellation state.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use reth_cli_util::cancellation::CancellationToken;
|
||||
/// use std::{thread, time::Duration};
|
||||
///
|
||||
/// let token = CancellationToken::new();
|
||||
/// let worker_token = token.clone();
|
||||
///
|
||||
/// let handle = thread::spawn(move || {
|
||||
/// while !worker_token.is_cancelled() {
|
||||
/// // Do work...
|
||||
/// thread::sleep(Duration::from_millis(100));
|
||||
/// }
|
||||
/// });
|
||||
///
|
||||
/// // Cancel from main thread
|
||||
/// token.cancel();
|
||||
/// handle.join().unwrap();
|
||||
/// ```
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CancellationToken {
|
||||
cancelled: Arc<AtomicBool>,
|
||||
}
|
||||
|
||||
impl CancellationToken {
|
||||
/// Creates a new cancellation token in the non-cancelled state.
|
||||
pub fn new() -> Self {
|
||||
Self { cancelled: Arc::new(AtomicBool::new(false)) }
|
||||
}
|
||||
|
||||
/// Signals cancellation to all holders of this token and its clones.
|
||||
///
|
||||
/// Once cancelled, the token cannot be reset. This operation is thread-safe
|
||||
/// and can be called multiple times without issue.
|
||||
pub fn cancel(&self) {
|
||||
self.cancelled.store(true, Ordering::Release);
|
||||
}
|
||||
|
||||
/// Checks whether cancellation has been requested.
|
||||
///
|
||||
/// Returns `true` if [`cancel`](Self::cancel) has been called on this token
|
||||
/// or any of its clones.
|
||||
pub fn is_cancelled(&self) -> bool {
|
||||
self.cancelled.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Creates a guard that automatically cancels this token when dropped.
|
||||
///
|
||||
/// This is useful for ensuring cancellation happens when a scope exits,
|
||||
/// either normally or via panic.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use reth_cli_util::cancellation::CancellationToken;
|
||||
///
|
||||
/// let token = CancellationToken::new();
|
||||
/// {
|
||||
/// let _guard = token.drop_guard();
|
||||
/// assert!(!token.is_cancelled());
|
||||
/// // Guard dropped here, triggering cancellation
|
||||
/// }
|
||||
/// assert!(token.is_cancelled());
|
||||
/// ```
|
||||
pub fn drop_guard(&self) -> CancellationGuard {
|
||||
CancellationGuard { token: self.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for CancellationToken {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// A guard that cancels its associated [`CancellationToken`] when dropped.
|
||||
///
|
||||
/// Created by calling [`CancellationToken::drop_guard`]. When this guard is dropped,
|
||||
/// it automatically calls [`cancel`](CancellationToken::cancel) on the token.
|
||||
#[derive(Debug)]
|
||||
pub struct CancellationGuard {
|
||||
token: CancellationToken,
|
||||
}
|
||||
|
||||
impl Drop for CancellationGuard {
|
||||
fn drop(&mut self) {
|
||||
self.token.cancel();
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,10 @@
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
|
||||
pub mod allocator;
|
||||
pub mod cancellation;
|
||||
|
||||
/// Helper function to load a secret key from a file.
|
||||
pub mod load_secret_key;
|
||||
pub use load_secret_key::{get_secret_key, parse_secret_key_from_hex};
|
||||
pub use load_secret_key::get_secret_key;
|
||||
|
||||
/// Cli parsers functions.
|
||||
pub mod parsers;
|
||||
|
||||
@@ -30,10 +30,6 @@ pub enum SecretKeyError {
|
||||
/// Path to the secret key file.
|
||||
secret_file: PathBuf,
|
||||
},
|
||||
|
||||
/// Invalid hex string format.
|
||||
#[error("invalid hex string: {0}")]
|
||||
InvalidHexString(String),
|
||||
}
|
||||
|
||||
/// Attempts to load a [`SecretKey`] from a specified path. If no file exists there, then it
|
||||
@@ -64,75 +60,3 @@ pub fn get_secret_key(secret_key_path: &Path) -> Result<SecretKey, SecretKeyErro
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses a [`SecretKey`] from a hex string.
|
||||
///
|
||||
/// The hex string can optionally start with "0x".
|
||||
pub fn parse_secret_key_from_hex(hex_str: &str) -> Result<SecretKey, SecretKeyError> {
|
||||
// Remove "0x" prefix if present
|
||||
let hex_str = hex_str.strip_prefix("0x").unwrap_or(hex_str);
|
||||
|
||||
// Decode the hex string
|
||||
let bytes = alloy_primitives::hex::decode(hex_str)
|
||||
.map_err(|e| SecretKeyError::InvalidHexString(e.to_string()))?;
|
||||
|
||||
// Parse into SecretKey
|
||||
SecretKey::from_slice(&bytes).map_err(SecretKeyError::SecretKeyDecodeError)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_parse_secret_key_from_hex_without_prefix() {
|
||||
// Valid 32-byte hex string (64 characters)
|
||||
let hex = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
|
||||
let result = parse_secret_key_from_hex(hex);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let secret_key = result.unwrap();
|
||||
assert_eq!(alloy_primitives::hex::encode(secret_key.secret_bytes()), hex);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_secret_key_from_hex_with_0x_prefix() {
|
||||
// Valid 32-byte hex string with 0x prefix
|
||||
let hex = "0x4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
|
||||
let result = parse_secret_key_from_hex(hex);
|
||||
assert!(result.is_ok());
|
||||
|
||||
let secret_key = result.unwrap();
|
||||
let expected = "4c0883a69102937d6231471b5dbb6204fe512961708279f8c5c58b3b9c4e8b8f";
|
||||
assert_eq!(alloy_primitives::hex::encode(secret_key.secret_bytes()), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_secret_key_from_hex_invalid_length() {
|
||||
// Invalid length (not 32 bytes)
|
||||
let hex = "4c0883a69102937d";
|
||||
let result = parse_secret_key_from_hex(hex);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_secret_key_from_hex_invalid_chars() {
|
||||
// Invalid hex characters
|
||||
let hex = "zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz";
|
||||
let result = parse_secret_key_from_hex(hex);
|
||||
assert!(result.is_err());
|
||||
|
||||
if let Err(SecretKeyError::InvalidHexString(_)) = result {
|
||||
// Expected error type
|
||||
} else {
|
||||
panic!("Expected InvalidHexString error");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_secret_key_from_hex_empty() {
|
||||
let hex = "";
|
||||
let result = parse_secret_key_from_hex(hex);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,16 +31,6 @@ pub fn parse_duration_from_secs_or_ms(
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to format a [Duration] to the format that can be parsed by
|
||||
/// [`parse_duration_from_secs_or_ms`].
|
||||
pub fn format_duration_as_secs_or_ms(duration: Duration) -> String {
|
||||
if duration.as_millis().is_multiple_of(1000) {
|
||||
format!("{}", duration.as_secs())
|
||||
} else {
|
||||
format!("{}ms", duration.as_millis())
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse [`BlockHashOrNumber`]
|
||||
pub fn hash_or_num_value_parser(value: &str) -> eyre::Result<BlockHashOrNumber, eyre::Error> {
|
||||
match B256::from_str(value) {
|
||||
|
||||
@@ -126,8 +126,7 @@ pub fn install() {
|
||||
libc::sigaltstack(&raw const alt_stack, ptr::null_mut());
|
||||
|
||||
let mut sa: libc::sigaction = mem::zeroed();
|
||||
sa.sa_sigaction =
|
||||
print_stack_trace as unsafe extern "C" fn(libc::c_int) as libc::sighandler_t;
|
||||
sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
|
||||
sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
|
||||
libc::sigemptyset(&raw mut sa.sa_mask);
|
||||
libc::sigaction(libc::SIGSEGV, &raw const sa, ptr::null_mut());
|
||||
|
||||
@@ -15,7 +15,6 @@ workspace = true
|
||||
reth-network-types.workspace = true
|
||||
reth-prune-types.workspace = true
|
||||
reth-stages-types.workspace = true
|
||||
reth-static-file-types.workspace = true
|
||||
|
||||
# serde
|
||||
serde = { workspace = true, optional = true }
|
||||
@@ -23,7 +22,7 @@ humantime-serde = { workspace = true, optional = true }
|
||||
|
||||
# toml
|
||||
toml = { workspace = true, optional = true }
|
||||
eyre.workspace = true
|
||||
eyre = { workspace = true, optional = true }
|
||||
|
||||
# value objects
|
||||
url.workspace = true
|
||||
@@ -32,6 +31,7 @@ url.workspace = true
|
||||
serde = [
|
||||
"dep:serde",
|
||||
"dep:toml",
|
||||
"dep:eyre",
|
||||
"dep:humantime-serde",
|
||||
"reth-network-types/serde",
|
||||
"reth-prune-types/serde",
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
use reth_network_types::{PeersConfig, SessionsConfig};
|
||||
use reth_prune_types::PruneModes;
|
||||
use reth_stages_types::ExecutionStageThresholds;
|
||||
use reth_static_file_types::StaticFileSegment;
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
@@ -31,9 +29,6 @@ pub struct Config {
|
||||
pub peers: PeersConfig,
|
||||
/// Configuration for peer sessions.
|
||||
pub sessions: SessionsConfig,
|
||||
/// Configuration for static files.
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub static_files: StaticFilesConfig,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -416,77 +411,6 @@ impl EtlConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Static files configuration.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct StaticFilesConfig {
|
||||
/// Number of blocks per file for each segment.
|
||||
pub blocks_per_file: BlocksPerFileConfig,
|
||||
}
|
||||
|
||||
/// Configuration for the number of blocks per file for each segment.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct BlocksPerFileConfig {
|
||||
/// Number of blocks per file for the headers segment.
|
||||
pub headers: Option<u64>,
|
||||
/// Number of blocks per file for the transactions segment.
|
||||
pub transactions: Option<u64>,
|
||||
/// Number of blocks per file for the receipts segment.
|
||||
pub receipts: Option<u64>,
|
||||
/// Number of blocks per file for the transaction senders segment.
|
||||
pub transaction_senders: Option<u64>,
|
||||
}
|
||||
|
||||
impl StaticFilesConfig {
|
||||
/// Validates the static files configuration.
|
||||
///
|
||||
/// Returns an error if any blocks per file value is zero.
|
||||
pub fn validate(&self) -> eyre::Result<()> {
|
||||
let BlocksPerFileConfig { headers, transactions, receipts, transaction_senders } =
|
||||
self.blocks_per_file;
|
||||
eyre::ensure!(headers != Some(0), "Headers segment blocks per file must be greater than 0");
|
||||
eyre::ensure!(
|
||||
transactions != Some(0),
|
||||
"Transactions segment blocks per file must be greater than 0"
|
||||
);
|
||||
eyre::ensure!(
|
||||
receipts != Some(0),
|
||||
"Receipts segment blocks per file must be greater than 0"
|
||||
);
|
||||
eyre::ensure!(
|
||||
transaction_senders != Some(0),
|
||||
"Transaction senders segment blocks per file must be greater than 0"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Converts the blocks per file configuration into a [`HashMap`] per segment.
|
||||
pub fn as_blocks_per_file_map(&self) -> HashMap<StaticFileSegment, u64> {
|
||||
let BlocksPerFileConfig { headers, transactions, receipts, transaction_senders } =
|
||||
self.blocks_per_file;
|
||||
|
||||
let mut map = HashMap::new();
|
||||
// Iterating over all possible segments allows us to do an exhaustive match here,
|
||||
// to not forget to configure new segments in the future.
|
||||
for segment in StaticFileSegment::iter() {
|
||||
let blocks_per_file = match segment {
|
||||
StaticFileSegment::Headers => headers,
|
||||
StaticFileSegment::Transactions => transactions,
|
||||
StaticFileSegment::Receipts => receipts,
|
||||
StaticFileSegment::TransactionSenders => transaction_senders,
|
||||
};
|
||||
|
||||
if let Some(blocks_per_file) = blocks_per_file {
|
||||
map.insert(segment, blocks_per_file);
|
||||
}
|
||||
}
|
||||
map
|
||||
}
|
||||
}
|
||||
|
||||
/// History stage configuration.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
//! Collection of methods for block validation.
|
||||
|
||||
use alloy_consensus::{BlockHeader as _, Transaction, EMPTY_OMMER_ROOT_HASH};
|
||||
use alloy_consensus::{
|
||||
constants::MAXIMUM_EXTRA_DATA_SIZE, BlockHeader as _, Transaction, EMPTY_OMMER_ROOT_HASH,
|
||||
};
|
||||
use alloy_eips::{eip4844::DATA_GAS_PER_BLOB, eip7840::BlobParams};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardfork, EthereumHardforks};
|
||||
use reth_consensus::{ConsensusError, TxGasLimitTooHighErr};
|
||||
@@ -223,9 +225,13 @@ where
|
||||
/// Validates that the EIP-4844 header fields exist and conform to the spec. This ensures that:
|
||||
///
|
||||
/// * `blob_gas_used` exists as a header field
|
||||
/// * `excess_blob_gas` exists as a header field
|
||||
/// * `parent_beacon_block_root` exists as a header field
|
||||
/// * `blob_gas_used` is a multiple of `DATA_GAS_PER_BLOB`
|
||||
/// * `excess_blob_gas` is a multiple of `DATA_GAS_PER_BLOB`
|
||||
/// * `blob_gas_used` doesn't exceed the max allowed blob gas based on the given params
|
||||
///
|
||||
/// Note: This does not enforce any restrictions on `blob_gas_used`
|
||||
pub fn validate_4844_header_standalone<H: BlockHeader>(
|
||||
header: &H,
|
||||
blob_params: BlobParams,
|
||||
@@ -258,12 +264,9 @@ pub fn validate_4844_header_standalone<H: BlockHeader>(
|
||||
/// From yellow paper: extraData: An arbitrary byte array containing data relevant to this block.
|
||||
/// This must be 32 bytes or fewer; formally Hx.
|
||||
#[inline]
|
||||
pub fn validate_header_extra_data<H: BlockHeader>(
|
||||
header: &H,
|
||||
max_size: usize,
|
||||
) -> Result<(), ConsensusError> {
|
||||
pub fn validate_header_extra_data<H: BlockHeader>(header: &H) -> Result<(), ConsensusError> {
|
||||
let extra_data_len = header.extra_data().len();
|
||||
if extra_data_len > max_size {
|
||||
if extra_data_len > MAXIMUM_EXTRA_DATA_SIZE {
|
||||
Err(ConsensusError::ExtraDataExceedsMax { len: extra_data_len })
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -500,21 +503,4 @@ mod tests {
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_header_extra_data_with_custom_limit() {
|
||||
// Test with default 32 bytes - should pass
|
||||
let header_32 = Header { extra_data: Bytes::from(vec![0; 32]), ..Default::default() };
|
||||
assert!(validate_header_extra_data(&header_32, 32).is_ok());
|
||||
|
||||
// Test exceeding default - should fail
|
||||
let header_33 = Header { extra_data: Bytes::from(vec![0; 33]), ..Default::default() };
|
||||
assert_eq!(
|
||||
validate_header_extra_data(&header_33, 32),
|
||||
Err(ConsensusError::ExtraDataExceedsMax { len: 33 })
|
||||
);
|
||||
|
||||
// Test with custom larger limit - should pass
|
||||
assert!(validate_header_extra_data(&header_33, 64).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use alloy_consensus::Header;
|
||||
use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256};
|
||||
use reth_execution_types::BlockExecutionResult;
|
||||
use reth_primitives_traits::{
|
||||
constants::{GAS_LIMIT_BOUND_DIVISOR, MAXIMUM_GAS_LIMIT_BLOCK, MINIMUM_GAS_LIMIT},
|
||||
constants::{MAXIMUM_GAS_LIMIT_BLOCK, MINIMUM_GAS_LIMIT},
|
||||
transaction::error::InvalidTransactionError,
|
||||
Block, GotExpected, GotExpectedBoxed, NodePrimitives, RecoveredBlock, SealedBlock,
|
||||
SealedHeader,
|
||||
@@ -349,7 +349,7 @@ pub enum ConsensusError {
|
||||
},
|
||||
|
||||
/// Error when the child gas limit exceeds the maximum allowed increase.
|
||||
#[error("child gas_limit {child_gas_limit} exceeds the max allowed increase ({parent_gas_limit}/{GAS_LIMIT_BOUND_DIVISOR})")]
|
||||
#[error("child gas_limit {child_gas_limit} max increase is {parent_gas_limit}/1024")]
|
||||
GasLimitInvalidIncrease {
|
||||
/// The parent gas limit.
|
||||
parent_gas_limit: u64,
|
||||
@@ -378,7 +378,7 @@ pub enum ConsensusError {
|
||||
},
|
||||
|
||||
/// Error when the child gas limit exceeds the maximum allowed decrease.
|
||||
#[error("child gas_limit {child_gas_limit} is below the max allowed decrease ({parent_gas_limit}/{GAS_LIMIT_BOUND_DIVISOR})")]
|
||||
#[error("child gas_limit {child_gas_limit} max decrease is {parent_gas_limit}/1024")]
|
||||
GasLimitInvalidDecrease {
|
||||
/// The parent gas limit.
|
||||
parent_gas_limit: u64,
|
||||
|
||||
@@ -61,42 +61,34 @@ where
|
||||
type Block = PrimitiveBlock;
|
||||
|
||||
async fn subscribe_blocks(&self, tx: Sender<Self::Block>) {
|
||||
loop {
|
||||
let Ok(mut stream) = self.full_block_stream().await.inspect_err(|err| {
|
||||
warn!(
|
||||
target: "consensus::debug-client",
|
||||
%err,
|
||||
url=%self.url,
|
||||
"Failed to subscribe to blocks",
|
||||
);
|
||||
}) else {
|
||||
return
|
||||
};
|
||||
let Ok(mut stream) = self.full_block_stream().await.inspect_err(|err| {
|
||||
warn!(
|
||||
target: "consensus::debug-client",
|
||||
%err,
|
||||
url=%self.url,
|
||||
"Failed to subscribe to blocks",
|
||||
);
|
||||
}) else {
|
||||
return
|
||||
};
|
||||
|
||||
while let Some(res) = stream.next().await {
|
||||
match res {
|
||||
Ok(block) => {
|
||||
if tx.send((self.convert)(block)).await.is_err() {
|
||||
// Channel closed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
target: "consensus::debug-client",
|
||||
%err,
|
||||
url=%self.url,
|
||||
"Failed to fetch a block",
|
||||
);
|
||||
while let Some(res) = stream.next().await {
|
||||
match res {
|
||||
Ok(block) => {
|
||||
if tx.send((self.convert)(block)).await.is_err() {
|
||||
// Channel closed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
warn!(
|
||||
target: "consensus::debug-client",
|
||||
%err,
|
||||
url=%self.url,
|
||||
"Failed to fetch a block",
|
||||
);
|
||||
}
|
||||
}
|
||||
// if stream terminated we want to re-establish it again
|
||||
debug!(
|
||||
target: "consensus::debug-client",
|
||||
url=%self.url,
|
||||
"Re-estbalishing block subscription",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
use node::NodeTestContext;
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_db::{test_utils::TempDatabase, DatabaseEnv};
|
||||
use reth_engine_local::LocalPayloadAttributesBuilder;
|
||||
use reth_network_api::test_utils::PeersHandleProvider;
|
||||
use reth_node_builder::{
|
||||
components::NodeComponentsBuilder,
|
||||
rpc::{EngineValidatorAddOn, RethRpcAddOns},
|
||||
FullNodeTypesAdapter, Node, NodeAdapter, NodeComponents, NodeTypes, NodeTypesWithDBAdapter,
|
||||
PayloadTypes,
|
||||
PayloadAttributesBuilder, PayloadTypes,
|
||||
};
|
||||
use reth_provider::providers::{BlockchainProvider, NodeTypesForProvider};
|
||||
use reth_tasks::TaskManager;
|
||||
@@ -53,6 +54,8 @@ pub async fn setup<N>(
|
||||
) -> eyre::Result<(Vec<NodeHelperType<N>>, TaskManager, Wallet)>
|
||||
where
|
||||
N: NodeBuilderHelper,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>:
|
||||
PayloadAttributesBuilder<<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes>,
|
||||
{
|
||||
E2ETestSetupBuilder::new(num_nodes, chain_spec, attributes_generator)
|
||||
.with_node_config_modifier(move |config| config.set_dev(is_dev))
|
||||
@@ -74,6 +77,8 @@ pub async fn setup_engine<N>(
|
||||
)>
|
||||
where
|
||||
N: NodeBuilderHelper,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>:
|
||||
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
|
||||
{
|
||||
setup_engine_with_connection::<N>(
|
||||
num_nodes,
|
||||
@@ -101,6 +106,8 @@ pub async fn setup_engine_with_connection<N>(
|
||||
)>
|
||||
where
|
||||
N: NodeBuilderHelper,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>:
|
||||
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
|
||||
{
|
||||
E2ETestSetupBuilder::new(num_nodes, chain_spec, attributes_generator)
|
||||
.with_tree_config_modifier(move |_| tree_config.clone())
|
||||
@@ -153,10 +160,13 @@ where
|
||||
>,
|
||||
ChainSpec: From<ChainSpec> + Clone,
|
||||
>,
|
||||
LocalPayloadAttributesBuilder<Self::ChainSpec>:
|
||||
PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<T> NodeBuilderHelper for T where
|
||||
impl<T> NodeBuilderHelper for T
|
||||
where
|
||||
Self: Default
|
||||
+ NodeTypesForProvider<
|
||||
Payload: PayloadTypes<
|
||||
@@ -177,6 +187,8 @@ impl<T> NodeBuilderHelper for T where
|
||||
Adapter<Self, BlockchainProvider<NodeTypesWithDBAdapter<Self, TmpDB>>>,
|
||||
>,
|
||||
ChainSpec: From<ChainSpec> + Clone,
|
||||
>
|
||||
>,
|
||||
LocalPayloadAttributesBuilder<Self::ChainSpec>:
|
||||
PayloadAttributesBuilder<<Self::Payload as PayloadTypes>::PayloadAttributes>,
|
||||
{
|
||||
}
|
||||
|
||||
@@ -4,18 +4,18 @@
|
||||
//! configurations through closures that modify `NodeConfig` and `TreeConfig`.
|
||||
|
||||
use crate::{node::NodeTestContext, wallet::Wallet, NodeBuilderHelper, NodeHelperType, TmpDB};
|
||||
use futures_util::future::TryJoinAll;
|
||||
use reth_chainspec::EthChainSpec;
|
||||
use reth_engine_local::LocalPayloadAttributesBuilder;
|
||||
use reth_node_builder::{
|
||||
EngineNodeLauncher, NodeBuilder, NodeConfig, NodeHandle, NodeTypes, NodeTypesWithDBAdapter,
|
||||
PayloadTypes,
|
||||
PayloadAttributesBuilder, PayloadTypes,
|
||||
};
|
||||
use reth_node_core::args::{DiscoveryArgs, NetworkArgs, RpcServerArgs};
|
||||
use reth_provider::providers::BlockchainProvider;
|
||||
use reth_rpc_server_types::RpcModuleSelection;
|
||||
use reth_tasks::TaskManager;
|
||||
use std::sync::Arc;
|
||||
use tracing::{span, Instrument, Level};
|
||||
use tracing::{span, Level};
|
||||
|
||||
/// Type alias for tree config modifier closure
|
||||
type TreeConfigModifier =
|
||||
@@ -37,6 +37,8 @@ where
|
||||
+ Sync
|
||||
+ Copy
|
||||
+ 'static,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>:
|
||||
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
|
||||
{
|
||||
num_nodes: usize,
|
||||
chain_spec: Arc<N::ChainSpec>,
|
||||
@@ -54,6 +56,8 @@ where
|
||||
+ Sync
|
||||
+ Copy
|
||||
+ 'static,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>:
|
||||
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
|
||||
{
|
||||
/// Creates a new builder with the required parameters.
|
||||
pub fn new(num_nodes: usize, chain_spec: Arc<N::ChainSpec>, attributes_generator: F) -> Self {
|
||||
@@ -118,71 +122,66 @@ where
|
||||
reth_node_api::TreeConfig::default()
|
||||
};
|
||||
|
||||
let mut nodes = (0..self.num_nodes)
|
||||
.map(async |idx| {
|
||||
// Create base node config
|
||||
let base_config = NodeConfig::new(self.chain_spec.clone())
|
||||
.with_network(network_config.clone())
|
||||
.with_unused_ports()
|
||||
.with_rpc(
|
||||
RpcServerArgs::default()
|
||||
.with_unused_ports()
|
||||
.with_http()
|
||||
.with_http_api(RpcModuleSelection::All),
|
||||
);
|
||||
|
||||
// Apply node config modifier if present
|
||||
let node_config = if let Some(modifier) = &self.node_config_modifier {
|
||||
modifier(base_config)
|
||||
} else {
|
||||
base_config
|
||||
};
|
||||
|
||||
let span = span!(Level::INFO, "node", idx);
|
||||
let node = N::default();
|
||||
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
|
||||
.testing_node(exec.clone())
|
||||
.with_types_and_provider::<N, BlockchainProvider<_>>()
|
||||
.with_components(node.components_builder())
|
||||
.with_add_ons(node.add_ons())
|
||||
.launch_with_fn(|builder| {
|
||||
let launcher = EngineNodeLauncher::new(
|
||||
builder.task_executor().clone(),
|
||||
builder.config().datadir(),
|
||||
tree_config.clone(),
|
||||
);
|
||||
builder.launch_with(launcher)
|
||||
})
|
||||
.instrument(span)
|
||||
.await?;
|
||||
|
||||
let node = NodeTestContext::new(node, self.attributes_generator).await?;
|
||||
|
||||
let genesis = node.block_hash(0);
|
||||
node.update_forkchoice(genesis, genesis).await?;
|
||||
|
||||
eyre::Ok(node)
|
||||
})
|
||||
.collect::<TryJoinAll<_>>()
|
||||
.await?;
|
||||
let mut nodes: Vec<NodeTestContext<_, _>> = Vec::with_capacity(self.num_nodes);
|
||||
|
||||
for idx in 0..self.num_nodes {
|
||||
let (prev, current) = nodes.split_at_mut(idx);
|
||||
let current = current.first_mut().unwrap();
|
||||
// Create base node config
|
||||
let base_config = NodeConfig::new(self.chain_spec.clone())
|
||||
.with_network(network_config.clone())
|
||||
.with_unused_ports()
|
||||
.with_rpc(
|
||||
RpcServerArgs::default()
|
||||
.with_unused_ports()
|
||||
.with_http()
|
||||
.with_http_api(RpcModuleSelection::All),
|
||||
);
|
||||
|
||||
// Apply node config modifier if present
|
||||
let node_config = if let Some(modifier) = &self.node_config_modifier {
|
||||
modifier(base_config)
|
||||
} else {
|
||||
base_config
|
||||
};
|
||||
|
||||
let span = span!(Level::INFO, "node", idx);
|
||||
let _enter = span.enter();
|
||||
let node = N::default();
|
||||
let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config)
|
||||
.testing_node(exec.clone())
|
||||
.with_types_and_provider::<N, BlockchainProvider<_>>()
|
||||
.with_components(node.components_builder())
|
||||
.with_add_ons(node.add_ons())
|
||||
.launch_with_fn(|builder| {
|
||||
let launcher = EngineNodeLauncher::new(
|
||||
builder.task_executor().clone(),
|
||||
builder.config().datadir(),
|
||||
tree_config.clone(),
|
||||
);
|
||||
builder.launch_with(launcher)
|
||||
})
|
||||
.await?;
|
||||
|
||||
let mut node = NodeTestContext::new(node, self.attributes_generator).await?;
|
||||
|
||||
let genesis = node.block_hash(0);
|
||||
node.update_forkchoice(genesis, genesis).await?;
|
||||
|
||||
// Connect nodes if requested
|
||||
if self.connect_nodes {
|
||||
if let Some(prev_idx) = idx.checked_sub(1) {
|
||||
prev[prev_idx].connect(current).await;
|
||||
if let Some(previous_node) = nodes.last_mut() {
|
||||
previous_node.connect(&mut node).await;
|
||||
}
|
||||
|
||||
// Connect last node with the first if there are more than two
|
||||
if idx + 1 == self.num_nodes &&
|
||||
self.num_nodes > 2 &&
|
||||
let Some(first) = prev.first_mut()
|
||||
let Some(first_node) = nodes.first_mut()
|
||||
{
|
||||
current.connect(first).await;
|
||||
node.connect(first_node).await;
|
||||
}
|
||||
}
|
||||
|
||||
nodes.push(node);
|
||||
}
|
||||
|
||||
Ok((nodes, tasks, Wallet::default().with_chain_id(self.chain_spec.chain().into())))
|
||||
@@ -197,6 +196,8 @@ where
|
||||
+ Sync
|
||||
+ Copy
|
||||
+ 'static,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>:
|
||||
PayloadAttributesBuilder<<N::Payload as PayloadTypes>::PayloadAttributes>,
|
||||
{
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("E2ETestSetupBuilder")
|
||||
|
||||
@@ -125,7 +125,7 @@ pub async fn setup_engine_with_chain_import(
|
||||
db.clone(),
|
||||
chain_spec.clone(),
|
||||
reth_provider::providers::StaticFileProvider::read_write(static_files_path.clone())?,
|
||||
)?;
|
||||
);
|
||||
|
||||
// Initialize genesis if needed
|
||||
reth_db_common::init::init_genesis(&provider_factory)?;
|
||||
@@ -324,8 +324,7 @@ mod tests {
|
||||
chain_spec.clone(),
|
||||
reth_provider::providers::StaticFileProvider::read_write(static_files_path.clone())
|
||||
.unwrap(),
|
||||
)
|
||||
.expect("failed to create provider factory");
|
||||
);
|
||||
|
||||
// Initialize genesis
|
||||
reth_db_common::init::init_genesis(&provider_factory).unwrap();
|
||||
@@ -385,8 +384,7 @@ mod tests {
|
||||
chain_spec.clone(),
|
||||
reth_provider::providers::StaticFileProvider::read_only(static_files_path, false)
|
||||
.unwrap(),
|
||||
)
|
||||
.expect("failed to create provider factory");
|
||||
);
|
||||
|
||||
let provider = provider_factory.database_provider_ro().unwrap();
|
||||
|
||||
@@ -477,8 +475,7 @@ mod tests {
|
||||
db.clone(),
|
||||
chain_spec.clone(),
|
||||
reth_provider::providers::StaticFileProvider::read_write(static_files_path).unwrap(),
|
||||
)
|
||||
.expect("failed to create provider factory");
|
||||
);
|
||||
|
||||
// Initialize genesis
|
||||
reth_db_common::init::init_genesis(&provider_factory).unwrap();
|
||||
|
||||
@@ -2,12 +2,13 @@
|
||||
|
||||
use crate::{
|
||||
testsuite::actions::{Action, ActionBox},
|
||||
NodeBuilderHelper,
|
||||
NodeBuilderHelper, PayloadAttributesBuilder,
|
||||
};
|
||||
use alloy_primitives::B256;
|
||||
use eyre::Result;
|
||||
use jsonrpsee::http_client::HttpClient;
|
||||
use reth_node_api::{EngineTypes, PayloadTypes};
|
||||
use reth_engine_local::LocalPayloadAttributesBuilder;
|
||||
use reth_node_api::{EngineTypes, NodeTypes, PayloadTypes};
|
||||
use reth_payload_builder::PayloadId;
|
||||
use std::{collections::HashMap, marker::PhantomData};
|
||||
pub mod actions;
|
||||
@@ -348,6 +349,9 @@ where
|
||||
pub async fn run<N>(mut self) -> Result<()>
|
||||
where
|
||||
N: NodeBuilderHelper<Payload = I>,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
|
||||
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
|
||||
>,
|
||||
{
|
||||
let mut setup = self.setup.take();
|
||||
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
//! Test setup utilities for configuring the initial state.
|
||||
|
||||
use crate::{setup_engine_with_connection, testsuite::Environment, NodeBuilderHelper};
|
||||
use crate::{
|
||||
setup_engine_with_connection, testsuite::Environment, NodeBuilderHelper,
|
||||
PayloadAttributesBuilder,
|
||||
};
|
||||
use alloy_eips::BlockNumberOrTag;
|
||||
use alloy_primitives::B256;
|
||||
use alloy_rpc_types_engine::{ForkchoiceState, PayloadAttributes};
|
||||
use eyre::{eyre, Result};
|
||||
use reth_chainspec::ChainSpec;
|
||||
use reth_engine_local::LocalPayloadAttributesBuilder;
|
||||
use reth_ethereum_primitives::Block;
|
||||
use reth_network_p2p::sync::{NetworkSyncUpdater, SyncState};
|
||||
use reth_node_api::{EngineTypes, NodeTypes, PayloadTypes, TreeConfig};
|
||||
@@ -134,19 +138,28 @@ where
|
||||
) -> Result<()>
|
||||
where
|
||||
N: NodeBuilderHelper<Payload = I>,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
|
||||
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
|
||||
>,
|
||||
{
|
||||
// Note: this future is quite large so we box it
|
||||
Box::pin(self.apply_with_import_(env, rlp_path)).await
|
||||
Box::pin(self.apply_with_import_::<N>(env, rlp_path)).await
|
||||
}
|
||||
|
||||
/// Apply setup using pre-imported chain data from RLP file
|
||||
async fn apply_with_import_(
|
||||
async fn apply_with_import_<N>(
|
||||
&mut self,
|
||||
env: &mut Environment<I>,
|
||||
rlp_path: &Path,
|
||||
) -> Result<()> {
|
||||
) -> Result<()>
|
||||
where
|
||||
N: NodeBuilderHelper<Payload = I>,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
|
||||
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
|
||||
>,
|
||||
{
|
||||
// Create nodes with imported chain data
|
||||
let import_result = self.create_nodes_with_import(rlp_path).await?;
|
||||
let import_result = self.create_nodes_with_import::<N>(rlp_path).await?;
|
||||
|
||||
// Extract node clients
|
||||
let mut node_clients = Vec::new();
|
||||
@@ -173,6 +186,9 @@ where
|
||||
pub async fn apply<N>(&mut self, env: &mut Environment<I>) -> Result<()>
|
||||
where
|
||||
N: NodeBuilderHelper<Payload = I>,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
|
||||
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
|
||||
>,
|
||||
{
|
||||
// Note: this future is quite large so we box it
|
||||
Box::pin(self.apply_::<N>(env)).await
|
||||
@@ -182,6 +198,9 @@ where
|
||||
async fn apply_<N>(&mut self, env: &mut Environment<I>) -> Result<()>
|
||||
where
|
||||
N: NodeBuilderHelper<Payload = I>,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
|
||||
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
|
||||
>,
|
||||
{
|
||||
// If import_rlp_path is set, use apply_with_import instead
|
||||
if let Some(rlp_path) = self.import_rlp_path.take() {
|
||||
@@ -240,10 +259,16 @@ where
|
||||
/// Note: Currently this only supports `EthereumNode` due to the import process
|
||||
/// being Ethereum-specific. The generic parameter N is kept for consistency
|
||||
/// with other methods but is not used.
|
||||
async fn create_nodes_with_import(
|
||||
async fn create_nodes_with_import<N>(
|
||||
&self,
|
||||
rlp_path: &Path,
|
||||
) -> Result<crate::setup_import::ChainImportResult> {
|
||||
) -> Result<crate::setup_import::ChainImportResult>
|
||||
where
|
||||
N: NodeBuilderHelper<Payload = I>,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
|
||||
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
|
||||
>,
|
||||
{
|
||||
let chain_spec =
|
||||
self.chain_spec.clone().ok_or_else(|| eyre!("Chain specification is required"))?;
|
||||
|
||||
@@ -276,6 +301,9 @@ where
|
||||
+ use<N, I>
|
||||
where
|
||||
N: NodeBuilderHelper<Payload = I>,
|
||||
LocalPayloadAttributesBuilder<N::ChainSpec>: PayloadAttributesBuilder<
|
||||
<<N as NodeTypes>::Payload as PayloadTypes>::PayloadAttributes,
|
||||
>,
|
||||
{
|
||||
move |timestamp| {
|
||||
let attributes = PayloadAttributes {
|
||||
|
||||
@@ -278,7 +278,7 @@ where
|
||||
let bundle_state_sorted = sort_bundle_state_for_comparison(re_executed_state);
|
||||
let output_state_sorted = sort_bundle_state_for_comparison(original_state);
|
||||
let filename = format!("{}.bundle_state.diff", block_prefix);
|
||||
let diff_path = self.save_diff(filename, &output_state_sorted, &bundle_state_sorted)?;
|
||||
let diff_path = self.save_diff(filename, &bundle_state_sorted, &output_state_sorted)?;
|
||||
|
||||
warn!(
|
||||
target: "engine::invalid_block_hooks::witness",
|
||||
@@ -308,13 +308,13 @@ where
|
||||
if let Some((original_updates, original_root)) = trie_updates {
|
||||
if re_executed_root != original_root {
|
||||
let filename = format!("{}.state_root.diff", block_prefix);
|
||||
let diff_path = self.save_diff(filename, &original_root, &re_executed_root)?;
|
||||
let diff_path = self.save_diff(filename, &re_executed_root, &original_root)?;
|
||||
warn!(target: "engine::invalid_block_hooks::witness", ?original_root, ?re_executed_root, diff_path = %diff_path.display(), "State root mismatch after re-execution");
|
||||
}
|
||||
|
||||
if re_executed_root != block.state_root() {
|
||||
let filename = format!("{}.header_state_root.diff", block_prefix);
|
||||
let diff_path = self.save_diff(filename, &block.state_root(), &re_executed_root)?;
|
||||
let diff_path = self.save_diff(filename, &re_executed_root, &block.state_root())?;
|
||||
warn!(target: "engine::invalid_block_hooks::witness", header_state_root=?block.state_root(), ?re_executed_root, diff_path = %diff_path.display(), "Re-executed state root does not match block state root");
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ reth-engine-primitives = { workspace = true, features = ["std"] }
|
||||
reth-ethereum-engine-primitives.workspace = true
|
||||
reth-payload-builder.workspace = true
|
||||
reth-payload-primitives.workspace = true
|
||||
reth-primitives-traits.workspace = true
|
||||
reth-storage-api.workspace = true
|
||||
reth-transaction-pool.workspace = true
|
||||
|
||||
@@ -44,5 +43,4 @@ op = [
|
||||
"dep:op-alloy-rpc-types-engine",
|
||||
"dep:reth-optimism-chainspec",
|
||||
"reth-payload-primitives/op",
|
||||
"reth-primitives-traits/op",
|
||||
]
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
//! Contains the implementation of the mining mode for the local engine.
|
||||
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_primitives::{TxHash, B256};
|
||||
use alloy_rpc_types_engine::ForkchoiceState;
|
||||
use eyre::OptionExt;
|
||||
@@ -9,7 +10,6 @@ use reth_payload_builder::PayloadBuilderHandle;
|
||||
use reth_payload_primitives::{
|
||||
BuiltPayload, EngineApiMessageVersion, PayloadAttributesBuilder, PayloadKind, PayloadTypes,
|
||||
};
|
||||
use reth_primitives_traits::{HeaderTy, SealedHeaderFor};
|
||||
use reth_storage_api::BlockReader;
|
||||
use reth_transaction_pool::TransactionPool;
|
||||
use std::{
|
||||
@@ -17,7 +17,7 @@ use std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
task::{Context, Poll},
|
||||
time::Duration,
|
||||
time::{Duration, UNIX_EPOCH},
|
||||
};
|
||||
use tokio::time::Interval;
|
||||
use tokio_stream::wrappers::ReceiverStream;
|
||||
@@ -106,8 +106,8 @@ pub struct LocalMiner<T: PayloadTypes, B, Pool: TransactionPool + Unpin> {
|
||||
mode: MiningMode<Pool>,
|
||||
/// The payload builder for the engine
|
||||
payload_builder: PayloadBuilderHandle<T>,
|
||||
/// Latest block in the chain so far.
|
||||
last_header: SealedHeaderFor<<T::BuiltPayload as BuiltPayload>::Primitives>,
|
||||
/// Timestamp for the next block.
|
||||
last_timestamp: u64,
|
||||
/// Stores latest mined blocks.
|
||||
last_block_hashes: VecDeque<B256>,
|
||||
}
|
||||
@@ -115,21 +115,18 @@ pub struct LocalMiner<T: PayloadTypes, B, Pool: TransactionPool + Unpin> {
|
||||
impl<T, B, Pool> LocalMiner<T, B, Pool>
|
||||
where
|
||||
T: PayloadTypes,
|
||||
B: PayloadAttributesBuilder<
|
||||
T::PayloadAttributes,
|
||||
HeaderTy<<T::BuiltPayload as BuiltPayload>::Primitives>,
|
||||
>,
|
||||
B: PayloadAttributesBuilder<<T as PayloadTypes>::PayloadAttributes>,
|
||||
Pool: TransactionPool + Unpin,
|
||||
{
|
||||
/// Spawns a new [`LocalMiner`] with the given parameters.
|
||||
pub fn new(
|
||||
provider: impl BlockReader<Header = HeaderTy<<T::BuiltPayload as BuiltPayload>::Primitives>>,
|
||||
provider: impl BlockReader,
|
||||
payload_attributes_builder: B,
|
||||
to_engine: ConsensusEngineHandle<T>,
|
||||
mode: MiningMode<Pool>,
|
||||
payload_builder: PayloadBuilderHandle<T>,
|
||||
) -> Self {
|
||||
let last_header =
|
||||
let latest_header =
|
||||
provider.sealed_header(provider.best_block_number().unwrap()).unwrap().unwrap();
|
||||
|
||||
Self {
|
||||
@@ -137,8 +134,8 @@ where
|
||||
to_engine,
|
||||
mode,
|
||||
payload_builder,
|
||||
last_block_hashes: VecDeque::from([last_header.hash()]),
|
||||
last_header,
|
||||
last_timestamp: latest_header.timestamp(),
|
||||
last_block_hashes: VecDeque::from([latest_header.hash()]),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,11 +193,19 @@ where
|
||||
/// Generates payload attributes for a new block, passes them to FCU and inserts built payload
|
||||
/// through newPayload.
|
||||
async fn advance(&mut self) -> eyre::Result<()> {
|
||||
let timestamp = std::cmp::max(
|
||||
self.last_timestamp.saturating_add(1),
|
||||
std::time::SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.expect("cannot be earlier than UNIX_EPOCH")
|
||||
.as_secs(),
|
||||
);
|
||||
|
||||
let res = self
|
||||
.to_engine
|
||||
.fork_choice_updated(
|
||||
self.forkchoice_state(),
|
||||
Some(self.payload_attributes_builder.build(&self.last_header)),
|
||||
Some(self.payload_attributes_builder.build(timestamp)),
|
||||
EngineApiMessageVersion::default(),
|
||||
)
|
||||
.await?;
|
||||
@@ -217,7 +222,8 @@ where
|
||||
eyre::bail!("No payload")
|
||||
};
|
||||
|
||||
let header = payload.block().sealed_header().clone();
|
||||
let block = payload.block();
|
||||
|
||||
let payload = T::block_to_payload(payload.block().clone());
|
||||
let res = self.to_engine.new_payload(payload).await?;
|
||||
|
||||
@@ -225,8 +231,8 @@ where
|
||||
eyre::bail!("Invalid payload")
|
||||
}
|
||||
|
||||
self.last_block_hashes.push_back(header.hash());
|
||||
self.last_header = header;
|
||||
self.last_timestamp = timestamp;
|
||||
self.last_block_hashes.push_back(block.hash());
|
||||
// ensure we keep at most 64 blocks
|
||||
if self.last_block_hashes.len() > 64 {
|
||||
self.last_block_hashes.pop_front();
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
//! The implementation of the [`PayloadAttributesBuilder`] for the
|
||||
//! [`LocalMiner`](super::LocalMiner).
|
||||
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_primitives::{Address, B256};
|
||||
use reth_chainspec::{EthChainSpec, EthereumHardforks};
|
||||
use reth_chainspec::EthereumHardforks;
|
||||
use reth_ethereum_engine_primitives::EthPayloadAttributes;
|
||||
use reth_payload_primitives::PayloadAttributesBuilder;
|
||||
use reth_primitives_traits::SealedHeader;
|
||||
use std::sync::Arc;
|
||||
|
||||
/// The attributes builder for local Ethereum payload.
|
||||
@@ -15,36 +13,21 @@ use std::sync::Arc;
|
||||
pub struct LocalPayloadAttributesBuilder<ChainSpec> {
|
||||
/// The chainspec
|
||||
pub chain_spec: Arc<ChainSpec>,
|
||||
|
||||
/// Whether to enforce increasing timestamp.
|
||||
pub enforce_increasing_timestamp: bool,
|
||||
}
|
||||
|
||||
impl<ChainSpec> LocalPayloadAttributesBuilder<ChainSpec> {
|
||||
/// Creates a new instance of the builder.
|
||||
pub const fn new(chain_spec: Arc<ChainSpec>) -> Self {
|
||||
Self { chain_spec, enforce_increasing_timestamp: true }
|
||||
}
|
||||
|
||||
/// Creates a new instance of the builder without enforcing increasing timestamps.
|
||||
pub fn without_increasing_timestamp(self) -> Self {
|
||||
Self { enforce_increasing_timestamp: false, ..self }
|
||||
Self { chain_spec }
|
||||
}
|
||||
}
|
||||
|
||||
impl<ChainSpec> PayloadAttributesBuilder<EthPayloadAttributes, ChainSpec::Header>
|
||||
impl<ChainSpec> PayloadAttributesBuilder<EthPayloadAttributes>
|
||||
for LocalPayloadAttributesBuilder<ChainSpec>
|
||||
where
|
||||
ChainSpec: EthChainSpec + EthereumHardforks + 'static,
|
||||
ChainSpec: Send + Sync + EthereumHardforks + 'static,
|
||||
{
|
||||
fn build(&self, parent: &SealedHeader<ChainSpec::Header>) -> EthPayloadAttributes {
|
||||
let mut timestamp =
|
||||
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs();
|
||||
|
||||
if self.enforce_increasing_timestamp {
|
||||
timestamp = std::cmp::max(parent.timestamp().saturating_add(1), timestamp);
|
||||
}
|
||||
|
||||
fn build(&self, timestamp: u64) -> EthPayloadAttributes {
|
||||
EthPayloadAttributes {
|
||||
timestamp,
|
||||
prev_randao: B256::random(),
|
||||
@@ -62,18 +45,14 @@ where
|
||||
}
|
||||
|
||||
#[cfg(feature = "op")]
|
||||
impl<ChainSpec>
|
||||
PayloadAttributesBuilder<op_alloy_rpc_types_engine::OpPayloadAttributes, ChainSpec::Header>
|
||||
impl<ChainSpec> PayloadAttributesBuilder<op_alloy_rpc_types_engine::OpPayloadAttributes>
|
||||
for LocalPayloadAttributesBuilder<ChainSpec>
|
||||
where
|
||||
ChainSpec: EthChainSpec + EthereumHardforks + 'static,
|
||||
ChainSpec: Send + Sync + EthereumHardforks + 'static,
|
||||
{
|
||||
fn build(
|
||||
&self,
|
||||
parent: &SealedHeader<ChainSpec::Header>,
|
||||
) -> op_alloy_rpc_types_engine::OpPayloadAttributes {
|
||||
fn build(&self, timestamp: u64) -> op_alloy_rpc_types_engine::OpPayloadAttributes {
|
||||
op_alloy_rpc_types_engine::OpPayloadAttributes {
|
||||
payload_attributes: self.build(parent),
|
||||
payload_attributes: self.build(timestamp),
|
||||
// Add dummy system transaction
|
||||
transactions: Some(vec![
|
||||
reth_optimism_chainspec::constants::TX_SET_L1_BLOCK_OP_MAINNET_BLOCK_124665056
|
||||
|
||||
@@ -30,7 +30,7 @@ fn default_account_worker_count() -> usize {
|
||||
}
|
||||
|
||||
/// The size of proof targets chunk to spawn in one multiproof calculation.
|
||||
pub const DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE: usize = 60;
|
||||
pub const DEFAULT_MULTIPROOF_TASK_CHUNK_SIZE: usize = 10;
|
||||
|
||||
/// Default number of reserved CPU cores for non-reth processes.
|
||||
///
|
||||
@@ -97,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 in parallel.
|
||||
/// Whether the host has enough parallelism to run state root task.
|
||||
has_enough_parallelism: bool,
|
||||
/// Whether multiproof task should chunk proof targets.
|
||||
multiproof_chunking_enabled: bool,
|
||||
@@ -385,17 +385,12 @@ impl TreeConfig {
|
||||
self
|
||||
}
|
||||
|
||||
/// Setter for whether or not the host has enough parallelism to run state root in parallel.
|
||||
/// Setter for has enough parallelism.
|
||||
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;
|
||||
|
||||
@@ -4,6 +4,7 @@ use crate::ForkchoiceStatus;
|
||||
use alloc::boxed::Box;
|
||||
use alloy_consensus::BlockHeader;
|
||||
use alloy_eips::BlockNumHash;
|
||||
use alloy_primitives::B256;
|
||||
use alloy_rpc_types_engine::ForkchoiceState;
|
||||
use core::{
|
||||
fmt::{Display, Formatter, Result},
|
||||
@@ -32,6 +33,8 @@ pub enum ConsensusEngineEvent<N: NodePrimitives = EthPrimitives> {
|
||||
CanonicalChainCommitted(Box<SealedHeader<N::BlockHeader>>, Duration),
|
||||
/// The consensus engine processed an invalid block.
|
||||
InvalidBlock(Box<SealedBlock<N::Block>>),
|
||||
/// The consensus engine is involved in live sync, and has specific progress
|
||||
LiveSyncProgress(ConsensusEngineLiveSyncProgress),
|
||||
}
|
||||
|
||||
impl<N: NodePrimitives> ConsensusEngineEvent<N> {
|
||||
@@ -70,9 +73,24 @@ where
|
||||
Self::InvalidBlock(block) => {
|
||||
write!(f, "InvalidBlock({:?})", block.num_hash())
|
||||
}
|
||||
Self::LiveSyncProgress(progress) => {
|
||||
write!(f, "LiveSyncProgress({progress:?})")
|
||||
}
|
||||
Self::BlockReceived(num_hash) => {
|
||||
write!(f, "BlockReceived({num_hash:?})")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Progress of the consensus engine during live sync.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum ConsensusEngineLiveSyncProgress {
|
||||
/// The consensus engine is downloading blocks from the network.
|
||||
DownloadingBlocks {
|
||||
/// The number of blocks remaining to download.
|
||||
remaining_blocks: u64,
|
||||
/// The target block hash to download.
|
||||
target: B256,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ use reth_payload_primitives::{
|
||||
EngineApiMessageVersion, EngineObjectValidationError, InvalidPayloadAttributesError,
|
||||
NewPayloadError, PayloadAttributes, PayloadOrAttributes, PayloadTypes,
|
||||
};
|
||||
use reth_primitives_traits::{Block, RecoveredBlock, SealedBlock};
|
||||
use reth_primitives_traits::{Block, RecoveredBlock};
|
||||
use reth_trie_common::HashedPostState;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
||||
@@ -131,21 +131,6 @@ pub trait PayloadValidator<Types: PayloadTypes>: Send + Sync + Unpin + 'static {
|
||||
/// The block type used by the engine.
|
||||
type Block: Block;
|
||||
|
||||
/// Converts the given payload into a sealed block without recovering signatures.
|
||||
///
|
||||
/// This function validates the payload and converts it into a [`SealedBlock`] which contains
|
||||
/// the block hash but does not perform signature recovery on transactions.
|
||||
///
|
||||
/// This is more efficient than [`Self::ensure_well_formed_payload`] when signature recovery
|
||||
/// is not needed immediately or will be performed later.
|
||||
///
|
||||
/// Implementers should ensure that the checks are done in the order that conforms with the
|
||||
/// engine-API specification.
|
||||
fn convert_payload_to_block(
|
||||
&self,
|
||||
payload: Types::ExecutionData,
|
||||
) -> Result<SealedBlock<Self::Block>, NewPayloadError>;
|
||||
|
||||
/// Ensures that the given payload does not violate any consensus rules that concern the block's
|
||||
/// layout.
|
||||
///
|
||||
@@ -157,10 +142,7 @@ pub trait PayloadValidator<Types: PayloadTypes>: Send + Sync + Unpin + 'static {
|
||||
fn ensure_well_formed_payload(
|
||||
&self,
|
||||
payload: Types::ExecutionData,
|
||||
) -> Result<RecoveredBlock<Self::Block>, NewPayloadError> {
|
||||
let sealed_block = self.convert_payload_to_block(payload)?;
|
||||
sealed_block.try_recover().map_err(|e| NewPayloadError::Other(e.into()))
|
||||
}
|
||||
) -> Result<RecoveredBlock<Self::Block>, NewPayloadError>;
|
||||
|
||||
/// Verifies payload post-execution w.r.t. hashed state updates.
|
||||
fn validate_block_post_execution_with_hashed_state(
|
||||
|
||||
@@ -229,15 +229,9 @@ fn bench_state_root(c: &mut Criterion) {
|
||||
black_box({
|
||||
let mut handle = payload_processor.spawn(
|
||||
Default::default(),
|
||||
(
|
||||
core::iter::empty::<
|
||||
Result<
|
||||
Recovered<TransactionSigned>,
|
||||
core::convert::Infallible,
|
||||
>,
|
||||
>(),
|
||||
std::convert::identity,
|
||||
),
|
||||
core::iter::empty::<
|
||||
Result<Recovered<TransactionSigned>, core::convert::Infallible>,
|
||||
>(),
|
||||
StateProviderBuilder::new(provider.clone(), genesis_hash, None),
|
||||
OverlayStateProviderFactory::new(provider),
|
||||
&TreeConfig::default(),
|
||||
|
||||
@@ -9,7 +9,7 @@ use reth_network_p2p::{
|
||||
full_block::{FetchFullBlockFuture, FetchFullBlockRangeFuture, FullBlockClient},
|
||||
BlockClient,
|
||||
};
|
||||
use reth_primitives_traits::{Block, SealedBlock};
|
||||
use reth_primitives_traits::{Block, RecoveredBlock, SealedBlock};
|
||||
use std::{
|
||||
cmp::{Ordering, Reverse},
|
||||
collections::{binary_heap::PeekMut, BinaryHeap, HashSet, VecDeque},
|
||||
@@ -44,7 +44,7 @@ pub enum DownloadAction {
|
||||
#[derive(Debug)]
|
||||
pub enum DownloadOutcome<B: Block> {
|
||||
/// Downloaded blocks.
|
||||
Blocks(Vec<SealedBlock<B>>),
|
||||
Blocks(Vec<RecoveredBlock<B>>),
|
||||
/// New download started.
|
||||
NewDownloadStarted {
|
||||
/// How many blocks are pending in this download.
|
||||
@@ -68,7 +68,7 @@ where
|
||||
inflight_block_range_requests: Vec<FetchFullBlockRangeFuture<Client>>,
|
||||
/// Buffered blocks from downloads - this is a min-heap of blocks, using the block number for
|
||||
/// ordering. This means the blocks will be popped from the heap with ascending block numbers.
|
||||
set_buffered_blocks: BinaryHeap<Reverse<OrderedSealedBlock<B>>>,
|
||||
set_buffered_blocks: BinaryHeap<Reverse<OrderedRecoveredBlock<B>>>,
|
||||
/// Engine download metrics.
|
||||
metrics: BlockDownloaderMetrics,
|
||||
/// Pending events to be emitted.
|
||||
@@ -226,8 +226,15 @@ where
|
||||
let mut request = self.inflight_block_range_requests.swap_remove(idx);
|
||||
if let Poll::Ready(blocks) = request.poll_unpin(cx) {
|
||||
trace!(target: "engine::download", len=?blocks.len(), first=?blocks.first().map(|b| b.num_hash()), last=?blocks.last().map(|b| b.num_hash()), "Received full block range, buffering");
|
||||
self.set_buffered_blocks
|
||||
.extend(blocks.into_iter().map(OrderedSealedBlock).map(Reverse));
|
||||
self.set_buffered_blocks.extend(
|
||||
blocks
|
||||
.into_iter()
|
||||
.map(|b| {
|
||||
let senders = b.senders().unwrap_or_default();
|
||||
OrderedRecoveredBlock(RecoveredBlock::new_sealed(b, senders))
|
||||
})
|
||||
.map(Reverse),
|
||||
);
|
||||
} else {
|
||||
// still pending
|
||||
self.inflight_block_range_requests.push(request);
|
||||
@@ -241,7 +248,8 @@ where
|
||||
}
|
||||
|
||||
// drain all unique element of the block buffer if there are any
|
||||
let mut downloaded_blocks = Vec::with_capacity(self.set_buffered_blocks.len());
|
||||
let mut downloaded_blocks: Vec<RecoveredBlock<B>> =
|
||||
Vec::with_capacity(self.set_buffered_blocks.len());
|
||||
while let Some(block) = self.set_buffered_blocks.pop() {
|
||||
// peek ahead and pop duplicates
|
||||
while let Some(peek) = self.set_buffered_blocks.peek_mut() {
|
||||
@@ -257,31 +265,32 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper type around [`SealedBlock`] that implements the [Ord]
|
||||
/// A wrapper type around [`RecoveredBlock`] that implements the [Ord]
|
||||
/// trait by block number.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct OrderedSealedBlock<B: Block>(SealedBlock<B>);
|
||||
struct OrderedRecoveredBlock<B: Block>(RecoveredBlock<B>);
|
||||
|
||||
impl<B: Block> PartialOrd for OrderedSealedBlock<B> {
|
||||
impl<B: Block> PartialOrd for OrderedRecoveredBlock<B> {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> Ord for OrderedSealedBlock<B> {
|
||||
impl<B: Block> Ord for OrderedRecoveredBlock<B> {
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.0.number().cmp(&other.0.number())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> From<SealedBlock<B>> for OrderedSealedBlock<B> {
|
||||
impl<B: Block> From<SealedBlock<B>> for OrderedRecoveredBlock<B> {
|
||||
fn from(block: SealedBlock<B>) -> Self {
|
||||
Self(block)
|
||||
let senders = block.senders().unwrap_or_default();
|
||||
Self(RecoveredBlock::new_sealed(block, senders))
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> From<OrderedSealedBlock<B>> for SealedBlock<B> {
|
||||
fn from(value: OrderedSealedBlock<B>) -> Self {
|
||||
impl<B: Block> From<OrderedRecoveredBlock<B>> for RecoveredBlock<B> {
|
||||
fn from(value: OrderedRecoveredBlock<B>) -> Self {
|
||||
value.0
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user