Compare commits

..

33 Commits

Author SHA1 Message Date
rakita
0f09a92acc fix: update gas_used/gas_refunded to ResultGas API 2026-02-12 16:47:57 +01:00
rakita
df8ba50cb7 chore: update revm/inspectors/alloy-evm patches and fix Bytecode flatten 2026-02-12 16:37:56 +01:00
rakita
61341a1342 Merge remote-tracking branch 'origin/main' into staging
# Conflicts:
#	Cargo.lock
#	crates/optimism/evm/src/lib.rs
2026-02-12 16:32:00 +01:00
rakita
3d02124e50 Merge origin/main into staging 2026-02-03 13:24:01 +01:00
rakita
953a1b3399 chore: update revm, revm-inspectors, alloy-evm to staging
- revm: 6aa06829d2caa2aa38606ed22b83354a7a7ff98e
- revm-inspectors: cc4f62d8c107b3d2dc42a220690a079cdfc8dfb1
- alloy-evm: 80e6a436ecade2b9bd950fd70bd0d6a45ef0a43e
2026-02-03 13:22:25 +01:00
rakita
a364989c61 Merge origin/main into staging 2026-02-03 13:14:17 +01:00
rakita
f4075f5926 Merge remote staging 2026-01-28 19:12:11 +01:00
rakita
211d3d2924 fix: adapt to revm hasher type changes 2026-01-28 19:11:48 +01:00
rakita
dbc5313a2c Merge origin/main into staging - update patches 2026-01-28 19:09:24 +01:00
rakita
345fc9cfd2 Merge branch 'main' into staging 2026-01-26 22:35:00 +01:00
rakita
33d61c30cb chore: update revm, revm-inspectors, alloy-evm to staging
Updates dependencies:
- revm to 300efbf3e391e1796f5210cd4506508e385a55d2
- revm-inspectors to 9464524e7d983c8601ba7af3048cd60575dad6bd
- alloy-evm to 1c8ff5a179e760517016ab4e4d5af1c4b5923594

Fixes:
- Add slot_num field to BlockEnv for EIP-7843 SLOTNUM opcode
- Update BlockHashCache API: use lowest() instead of keys()
2026-01-26 21:32:08 +01:00
DaniPopes
beb0c5e527 chore: reduce number of nightly builds (#21446) 2026-01-26 21:30:39 +01:00
DaniPopes
73c39279b1 chore: remove unused docker from makefile (#21445) 2026-01-26 21:30:39 +01:00
ethfanWilliam
2fd0a703e2 fix(stages): retain RocksDB TempDir in TestStageDB to prevent premature deletion (#21444) 2026-01-26 21:30:39 +01:00
Dan Cline
7dcd77de95 fix(pruner): prune account and storage changeset static files (#21346) 2026-01-26 21:30:39 +01:00
Dan Cline
da6e6afe78 chore(metrics): add a gas_last metric similar to new_payload_last (#21437) 2026-01-26 21:30:39 +01:00
Brian Picciano
04d9a33c68 refactor(trie): always use ParallelSparseTrie, deprecate config flags (#21435) 2026-01-26 21:30:39 +01:00
Arsenii Kulikov
be1f657b3c perf: use shared channel for prewarm workers (#21429) 2026-01-26 21:30:39 +01:00
Rez
cab4cbf0ea feat: configurable EVM execution limits (#21088)
Co-authored-by: Arsenii Kulikov <klkvrr@gmail.com>
2026-01-26 21:30:39 +01:00
figtracer
29f51abb22 feat(rpc): add transaction hash caching to EthStateCache (#21180)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Matthias Seitz
dd0ee0709c fix(rpc): add block timestamp validation in eth_simulateV1 (#21397)
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Matthias Seitz
dcd9e50663 fix(rpc): use correct error codes for eth_simulateV1 reverts and halts (#21412)
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Matthias Seitz
59d11378b9 fix(rpc): add block number validation in eth_simulateV1 (#21396)
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Matthias Seitz
70ceb755ad fix(rpc): populate block_hash in eth_simulateV1 logs (#21413)
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Matthias Seitz
a49991c766 feat(cli): make stopping on invalid block the default for reth import (#21403)
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Matthias Seitz
0a77c2aae1 feat(rpc): implement movePrecompileToAddress for eth_simulateV1 (#21414)
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Matthias Seitz
93adbf82a0 fix(rpc): set prevrandao to zero for eth_simulateV1 simulated blocks (#21399)
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Matthias Seitz
ce08b6f265 fix(rpc): cap simulate_v1 default gas limit to RPC gas cap (#21402)
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Matthias Seitz
b03a704a1e fix(engine): only warn for critical capability mismatches (#21398)
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
Andrey Kolishchak
a51e03fce6 fix(net): FetchFullBlockRangeFuture can get stuck forever after partial body fetch + error (#21411)
Co-authored-by: Matthias Seitz <matthias.seitz@outlook.de>
Co-authored-by: Amp <amp@ampcode.com>
2026-01-26 21:30:39 +01:00
rakita
57d7c98f66 chore: merge main and update alloy-evm staging patch 2026-01-26 12:39:58 +01:00
rakita
5d9a43f2d4 Merge remote-tracking branch 'origin/main' into staging 2026-01-26 12:36:44 +01:00
rakita
defd0e8e5c Bump revm to staging and fix breaking changes
- Patch revm and all sub-crates to staging commit 0dc217a9
- Patch revm-inspectors to staging commit fccc4ac5
- Patch alloy-evm to staging commit 625ccc0f
- Add slot_num field to BlockEnv initializers
- Update BlockHashCache usage (no longer has keys method)
2026-01-26 02:41:26 +01:00
103 changed files with 761 additions and 4839 deletions

View File

@@ -1,4 +0,0 @@
---
---
Improved nightly Docker build failure Slack notification with more detailed formatting and context.

View File

@@ -1,5 +0,0 @@
---
reth-node-events: patch
---
Updated consensus engine log message to be more accurate about received updates.

View File

@@ -1,9 +0,0 @@
---
reth-network-api: minor
reth-network-types: minor
reth-network: minor
reth-node-core: minor
reth: minor
---
Added optional ENR fork ID enforcement to filter out peers from incompatible networks during peer discovery, controlled by the `--enforce-enr-fork-id` CLI flag.

2
.github/CODEOWNERS vendored
View File

@@ -38,7 +38,7 @@ crates/storage/libmdbx-rs/ @shekhirin
crates/storage/nippy-jar/ @joshieDo @shekhirin
crates/storage/provider/ @joshieDo @shekhirin @yongkangc
crates/storage/storage-api/ @joshieDo
crates/tasks/ @mattsse @DaniPopes
crates/tasks/ @mattsse
crates/tokio-util/ @mattsse
crates/tracing/ @mattsse @shekhirin
crates/tracing-otlp/ @mattsse @Rjected

View File

@@ -117,18 +117,6 @@ jobs:
- name: Slack Webhook Action
uses: rtCamp/action-slack-notify@v2
env:
SLACK_COLOR: danger
SLACK_ICON_EMOJI: ":rotating_light:"
SLACK_USERNAME: "GitHub Actions"
SLACK_TITLE: ":rotating_light: Nightly Docker Build Failed"
SLACK_MESSAGE: |
The scheduled nightly Docker build failed.
*Commit:* `${{ github.sha }}`
*Branch:* `${{ github.ref_name }}`
*Run:* <https://github.com/paradigmxyz/reth/actions/runs/${{ github.run_id }}|View logs>
*Action required:* Re-run the workflow or investigate the build failure.
SLACK_FOOTER: "paradigmxyz/reth · docker.yml"
MSG_MINIMAL: true
SLACK_COLOR: ${{ job.status }}
SLACK_MESSAGE: "Failed run: https://github.com/paradigmxyz/reth/actions/runs/${{ github.run_id }}"
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK_URL }}

245
Cargo.lock generated
View File

@@ -290,8 +290,7 @@ dependencies = [
[[package]]
name = "alloy-evm"
version = "0.27.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ccfe6d724ceabd5518350cfb34f17dd3a6c3cc33579eee94d98101d3a511ff"
source = "git+https://github.com/alloy-rs/evm?rev=742dc14749ea0279c03ca27b1c26f26ac19fbefb#742dc14749ea0279c03ca27b1c26f26ac19fbefb"
dependencies = [
"alloy-consensus",
"alloy-eips",
@@ -568,7 +567,7 @@ checksum = "ce8849c74c9ca0f5a03da1c865e3eb6f768df816e67dd3721a398a8a7e398011"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -818,7 +817,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -835,7 +834,7 @@ dependencies = [
"proc-macro2",
"quote",
"sha3",
"syn 2.0.114",
"syn 2.0.115",
"syn-solidity",
]
@@ -851,7 +850,7 @@ dependencies = [
"macro-string",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
"syn-solidity",
]
@@ -983,7 +982,7 @@ dependencies = [
"darling 0.21.3",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1077,7 +1076,7 @@ dependencies = [
"proc-macro-error2",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1219,7 +1218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60"
dependencies = [
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1257,7 +1256,7 @@ dependencies = [
"num-traits",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1346,7 +1345,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1462,7 +1461,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1473,7 +1472,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1511,7 +1510,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1647,7 +1646,7 @@ dependencies = [
"regex",
"rustc-hash",
"shlex",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1854,7 +1853,7 @@ dependencies = [
"cow-utils",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
"synstructure",
]
@@ -1910,7 +1909,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -1993,7 +1992,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -2211,9 +2210,9 @@ dependencies = [
[[package]]
name = "clap"
version = "4.5.57"
version = "4.5.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a"
checksum = "63be97961acde393029492ce0be7a1af7e323e6bae9511ebfac33751be5e6806"
dependencies = [
"clap_builder",
"clap_derive",
@@ -2221,9 +2220,9 @@ dependencies = [
[[package]]
name = "clap_builder"
version = "4.5.57"
version = "4.5.58"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238"
checksum = "7f13174bda5dfd69d7e947827e5af4b0f2f94a4a3ee92912fba07a66150f21e2"
dependencies = [
"anstream",
"anstyle",
@@ -2240,14 +2239,14 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
name = "clap_lex"
version = "0.7.7"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
[[package]]
name = "cmake"
@@ -2787,7 +2786,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -2844,7 +2843,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -2859,7 +2858,7 @@ dependencies = [
"quote",
"serde",
"strsim",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -2872,7 +2871,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -2883,7 +2882,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead"
dependencies = [
"darling_core 0.20.11",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -2894,7 +2893,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81"
dependencies = [
"darling_core 0.21.3",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -2905,7 +2904,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [
"darling_core 0.23.0",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -2947,7 +2946,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ab67060fc6b8ef687992d439ca0fa36e7ed17e9a0b16b25b601e8757df720de"
dependencies = [
"data-encoding",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3006,7 +3005,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3017,7 +3016,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3038,7 +3037,7 @@ dependencies = [
"darling 0.20.11",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3048,7 +3047,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c"
dependencies = [
"derive_builder_core",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3070,7 +3069,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustc_version 0.4.1",
"syn 2.0.114",
"syn 2.0.115",
"unicode-xid",
]
@@ -3196,7 +3195,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3243,7 +3242,7 @@ checksum = "1ec431cd708430d5029356535259c5d645d60edd3d39c54e5eea9782d46caa7d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3295,7 +3294,7 @@ dependencies = [
"enum-ordinalize",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3402,7 +3401,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3422,7 +3421,7 @@ checksum = "8ca9601fb2d62598ee17836250842873a413586e5d7ed88b356e38ddbb0ec631"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3442,7 +3441,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3524,7 +3523,7 @@ dependencies = [
"darling 0.20.11",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -3536,7 +3535,7 @@ dependencies = [
"darling 0.23.0",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -4031,7 +4030,7 @@ checksum = "6dc7a9cb3326bafb80642c5ce99b39a2c0702d4bfa8ee8a3e773791a6cbe2407"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -4199,7 +4198,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -4954,7 +4953,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -5073,7 +5072,7 @@ dependencies = [
"indoc",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -5350,7 +5349,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -5582,7 +5581,7 @@ checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
dependencies = [
"bitflags 2.10.0",
"libc",
"redox_syscall 0.7.0",
"redox_syscall 0.7.1",
]
[[package]]
@@ -5757,7 +5756,7 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -5781,7 +5780,7 @@ checksum = "757aee279b8bdbb9f9e676796fd459e4207a1f986e87886700abf589f5abf771"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -5829,13 +5828,13 @@ dependencies = [
[[package]]
name = "metrics-derive"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37a87f4b19620e4c561f7b48f5e6ca085b1780def671696a6a3d9d0c137360ec"
checksum = "161ab904c2c62e7bda0f7562bf22f96440ca35ff79e66c800cbac298f2f4f5ec"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -5968,7 +5967,7 @@ checksum = "59b43b4fd69e3437618106f7754f34021b831a514f9e1a98ae863cabcd8d8dad"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -6224,7 +6223,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -6424,8 +6423,7 @@ dependencies = [
[[package]]
name = "op-revm"
version = "15.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79c92b75162c2ed1661849fa51683b11254a5b661798360a2c24be918edafd40"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"auto_impl",
"revm",
@@ -6606,7 +6604,7 @@ dependencies = [
"proc-macro-crate",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -6721,7 +6719,7 @@ dependencies = [
"phf_shared",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -6750,7 +6748,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -6891,7 +6889,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
dependencies = [
"proc-macro2",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -6942,7 +6940,7 @@ dependencies = [
"proc-macro-error-attr2",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -7015,7 +7013,7 @@ checksum = "fb6dc647500e84a25a85b100e76c85b8ace114c209432dc174f20aac11d4ed6c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -7038,7 +7036,7 @@ dependencies = [
"itertools 0.14.0",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -7387,9 +7385,9 @@ dependencies = [
[[package]]
name = "redox_syscall"
version = "0.7.0"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27"
checksum = "35985aa610addc02e24fc232012c86fd11f14111180f902b67e2d5331f8ebf2b"
dependencies = [
"bitflags 2.10.0",
]
@@ -7433,7 +7431,7 @@ checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -7889,7 +7887,7 @@ dependencies = [
"proc-macro2",
"quote",
"similar-asserts",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -10673,8 +10671,7 @@ dependencies = [
[[package]]
name = "revm"
version = "34.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2aabdebaa535b3575231a88d72b642897ae8106cf6b0d12eafc6bfdf50abfc7"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"revm-bytecode",
"revm-context",
@@ -10692,8 +10689,7 @@ dependencies = [
[[package]]
name = "revm-bytecode"
version = "8.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74d1e5c1eaa44d39d537f668bc5c3409dc01e5c8be954da6c83370bbdf006457"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"bitvec",
"phf",
@@ -10704,8 +10700,7 @@ dependencies = [
[[package]]
name = "revm-context"
version = "13.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "892ff3e6a566cf8d72ffb627fdced3becebbd9ba64089c25975b9b028af326a5"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"bitvec",
"cfg-if",
@@ -10721,8 +10716,7 @@ dependencies = [
[[package]]
name = "revm-context-interface"
version = "14.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57f61cc6d23678c4840af895b19f8acfbbd546142ec8028b6526c53cc1c16c98"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"alloy-eip2930",
"alloy-eip7702",
@@ -10737,8 +10731,7 @@ dependencies = [
[[package]]
name = "revm-database"
version = "10.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "529528d0b05fe646be86223032c3e77aa8b05caa2a35447d538c55965956a511"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"alloy-eips",
"revm-bytecode",
@@ -10751,8 +10744,7 @@ dependencies = [
[[package]]
name = "revm-database-interface"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7bf93ac5b91347c057610c0d96e923db8c62807e03f036762d03e981feddc1d"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"auto_impl",
"either",
@@ -10765,10 +10757,10 @@ dependencies = [
[[package]]
name = "revm-handler"
version = "15.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cd0e43e815a85eded249df886c4badec869195e70cdd808a13cfca2794622d2"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"auto_impl",
"bitflags 2.10.0",
"derive-where",
"revm-bytecode",
"revm-context",
@@ -10784,8 +10776,7 @@ dependencies = [
[[package]]
name = "revm-inspector"
version = "15.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f3ccad59db91ef93696536a0dbaf2f6f17cfe20d4d8843ae118edb7e97947ef"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"auto_impl",
"either",
@@ -10802,8 +10793,7 @@ dependencies = [
[[package]]
name = "revm-inspectors"
version = "0.34.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e435414e9de50a1b930da602067c76365fea2fea11e80ceb50783c94ddd127f"
source = "git+https://github.com/paradigmxyz/revm-inspectors?rev=e80e2eab72dfa18011e6a99abd37027290a46e83#e80e2eab72dfa18011e6a99abd37027290a46e83"
dependencies = [
"alloy-primitives",
"alloy-rpc-types-eth",
@@ -10822,8 +10812,7 @@ dependencies = [
[[package]]
name = "revm-interpreter"
version = "32.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11406408597bc249392d39295831c4b641b3a6f5c471a7c41104a7a1e3564c07"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"revm-bytecode",
"revm-context-interface",
@@ -10835,8 +10824,7 @@ dependencies = [
[[package]]
name = "revm-precompile"
version = "32.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50c1285c848d240678bf69cb0f6179ff5a4aee6fc8e921d89708087197a0aff3"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"ark-bls12-381",
"ark-bn254",
@@ -10859,8 +10847,7 @@ dependencies = [
[[package]]
name = "revm-primitives"
version = "22.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba580c56a8ec824a64f8a1683577876c2e1dbe5247044199e9b881421ad5dcf9"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"alloy-primitives",
"num_enum",
@@ -10871,8 +10858,7 @@ dependencies = [
[[package]]
name = "revm-state"
version = "9.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "311720d4f0f239b041375e7ddafdbd20032a33b7bae718562ea188e188ed9fd3"
source = "git+https://github.com/bluealloy/revm?rev=33330a285e621b9170c30a21cfea9ab32e2a2169#33330a285e621b9170c30a21cfea9ab32e2a2169"
dependencies = [
"alloy-eip7928",
"bitflags 2.10.0",
@@ -11018,7 +11004,7 @@ dependencies = [
"regex",
"relative-path",
"rustc_version 0.4.1",
"syn 2.0.114",
"syn 2.0.115",
"unicode-ident",
]
@@ -11458,7 +11444,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -11535,7 +11521,7 @@ dependencies = [
"darling 0.21.3",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -11848,7 +11834,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -11870,9 +11856,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.114"
version = "2.0.115"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
checksum = "6e614ed320ac28113fa64972c4262d5dbc89deacdfd00c34a3e4cea073243c12"
dependencies = [
"proc-macro2",
"quote",
@@ -11888,7 +11874,7 @@ dependencies = [
"paste",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -11908,7 +11894,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -11985,7 +11971,7 @@ dependencies = [
"cfg-if",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -11996,7 +11982,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
"test-case-core",
]
@@ -12036,7 +12022,7 @@ dependencies = [
"prettyplease",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -12084,7 +12070,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -12095,7 +12081,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -12242,7 +12228,7 @@ checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -12493,7 +12479,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -12687,7 +12673,7 @@ dependencies = [
"darling 0.20.11",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -12980,7 +12966,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -13093,7 +13079,7 @@ dependencies = [
"bumpalo",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
"wasm-bindgen-shared",
]
@@ -13313,7 +13299,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -13324,7 +13310,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -13716,7 +13702,7 @@ dependencies = [
"heck",
"indexmap 2.13.0",
"prettyplease",
"syn 2.0.114",
"syn 2.0.115",
"wasm-metadata",
"wit-bindgen-core",
"wit-component",
@@ -13732,7 +13718,7 @@ dependencies = [
"prettyplease",
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
"wit-bindgen-core",
"wit-bindgen-rust",
]
@@ -13855,7 +13841,7 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
"synstructure",
]
@@ -13876,7 +13862,7 @@ checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -13896,7 +13882,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
"synstructure",
]
@@ -13917,7 +13903,7 @@ checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
@@ -13951,14 +13937,14 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.114",
"syn 2.0.115",
]
[[package]]
name = "zmij"
version = "1.0.20"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7"
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
[[package]]
name = "zstd"
@@ -13987,3 +13973,8 @@ dependencies = [
"cc",
"pkg-config",
]
[[patch.unused]]
name = "alloy-op-evm"
version = "0.27.2"
source = "git+https://github.com/alloy-rs/evm?rev=742dc14749ea0279c03ca27b1c26f26ac19fbefb#742dc14749ea0279c03ca27b1c26f26ac19fbefb"

View File

@@ -711,6 +711,28 @@ vergen-git2 = "9.1.0"
ipnet = "2.11"
[patch.crates-io]
# revm staging patches
revm = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
op-revm = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-bytecode = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-context = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-context-interface = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-database = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-database-interface = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-handler = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-inspector = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-interpreter = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-precompile = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-primitives = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
revm-state = { git = "https://github.com/bluealloy/revm", rev = "33330a285e621b9170c30a21cfea9ab32e2a2169" }
# revm-inspectors staging patch
revm-inspectors = { git = "https://github.com/paradigmxyz/revm-inspectors", rev = "e80e2eab72dfa18011e6a99abd37027290a46e83" }
# alloy-evm staging patches
alloy-evm = { git = "https://github.com/alloy-rs/evm", rev = "742dc14749ea0279c03ca27b1c26f26ac19fbefb" }
alloy-op-evm = { git = "https://github.com/alloy-rs/evm", rev = "742dc14749ea0279c03ca27b1c26f26ac19fbefb" }
# alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
# alloy-contract = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }
# alloy-eips = { git = "https://github.com/alloy-rs/alloy", rev = "3049f232fbb44d1909883e154eb38ec5962f53a3" }

View File

@@ -192,15 +192,6 @@ impl Command {
parent_header = block.header;
parent_hash = block_hash;
blocks_processed += 1;
let progress = match mode {
RampMode::Blocks(total) => format!("{blocks_processed}/{total}"),
RampMode::TargetGasLimit(target) => {
let pct = (parent_header.gas_limit as f64 / target as f64 * 100.0).min(100.0);
format!("{pct:.1}%")
}
};
info!(target: "reth-bench", progress, block_number = parent_header.number, gas_limit = parent_header.gas_limit, "Block processed");
}
let final_gas_limit = parent_header.gas_limit;

View File

@@ -153,7 +153,6 @@ impl Command {
..
} = BenchContext::new(&self.benchmark, self.rpc_url).await?;
let total_blocks = benchmark_mode.total_blocks();
let buffer_size = self.rpc_block_buffer_size;
// Use a oneshot channel to propagate errors from the spawned task
@@ -207,7 +206,6 @@ impl Command {
});
let mut results = Vec::new();
let mut blocks_processed = 0u64;
let total_benchmark_duration = Instant::now();
let mut total_wait_time = Duration::ZERO;
@@ -251,13 +249,8 @@ impl Command {
// Exclude time spent waiting on the block prefetch channel from the benchmark duration.
// We want to measure engine throughput, not RPC fetch latency.
blocks_processed += 1;
let current_duration = total_benchmark_duration.elapsed() - total_wait_time;
let progress = match total_blocks {
Some(total) => format!("{blocks_processed}/{total}"),
None => format!("{blocks_processed}"),
};
info!(target: "reth-bench", progress, %combined_result);
info!(target: "reth-bench", %combined_result);
if let Some(w) = &mut waiter {
w.on_block(block_number).await?;

View File

@@ -52,7 +52,6 @@ impl Command {
..
} = BenchContext::new(&self.benchmark, self.rpc_url).await?;
let total_blocks = benchmark_mode.total_blocks();
let buffer_size = self.rpc_block_buffer_size;
// Use a oneshot channel to propagate errors from the spawned task
@@ -83,8 +82,8 @@ impl Command {
}
});
// put results in a summary vec so they can be printed at the end
let mut results = Vec::new();
let mut blocks_processed = 0u64;
let total_benchmark_duration = Instant::now();
let mut total_wait_time = Duration::ZERO;
@@ -106,12 +105,7 @@ impl Command {
call_new_payload(&auth_provider, version, params).await?;
let new_payload_result = NewPayloadResult { gas_used, latency: start.elapsed() };
blocks_processed += 1;
let progress = match total_blocks {
Some(total) => format!("{blocks_processed}/{total}"),
None => format!("{blocks_processed}"),
};
info!(target: "reth-bench", progress, %new_payload_result);
info!(target: "reth-bench", %new_payload_result);
// current duration since the start of the benchmark minus the time
// waiting for blocks

View File

@@ -341,8 +341,7 @@ impl Command {
};
let current_duration = total_benchmark_duration.elapsed();
let progress = format!("{}/{}", i + 1, payloads.len());
info!(target: "reth-bench", progress, %combined_result);
info!(target: "reth-bench", %combined_result);
if let Some(w) = &mut waiter {
w.on_block(block_number).await?;

View File

@@ -20,19 +20,6 @@ impl BenchMode {
}
}
/// Returns the total number of blocks in the benchmark, if known.
///
/// For [`BenchMode::Range`] this is the length of the range.
/// For [`BenchMode::Continuous`] the total is unbounded, so `None` is returned.
pub const fn total_blocks(&self) -> Option<u64> {
match self {
Self::Continuous(_) => None,
Self::Range(range) => {
Some(range.end().saturating_sub(*range.start()).saturating_add(1))
}
}
}
/// Create a [`BenchMode`] from optional `from` and `to` fields.
pub fn new(from: Option<u64>, to: Option<u64>, latest_block: u64) -> Result<Self, eyre::Error> {
// If neither `--from` nor `--to` are provided, we will run the benchmark continuously,

View File

@@ -1061,14 +1061,6 @@ mod tests {
) -> ProviderResult<Option<StorageValue>> {
Ok(None)
}
fn storage_by_hashed_key(
&self,
_address: Address,
_hashed_storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
Ok(None)
}
}
impl BytecodeReader for MockStateProvider {

View File

@@ -223,26 +223,6 @@ impl<N: NodePrimitives> StateProvider for MemoryOverlayStateProviderRef<'_, N> {
self.historical.storage(address, storage_key)
}
fn storage_by_hashed_key(
&self,
address: Address,
hashed_storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
let hashed_address = keccak256(address);
let state = &self.trie_input().state;
if let Some(hs) = state.storages.get(&hashed_address) {
if let Some(value) = hs.storage.get(&hashed_storage_key) {
return Ok(Some(*value));
}
if hs.wiped {
return Ok(Some(StorageValue::ZERO));
}
}
self.historical.storage_by_hashed_key(address, hashed_storage_key)
}
}
impl<N: NodePrimitives> BytecodeReader for MemoryOverlayStateProviderRef<'_, N> {

View File

@@ -5,7 +5,6 @@ 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 reth_storage_api::StorageSettingsCache;
use std::time::{Duration, Instant};
use tracing::info;
@@ -23,94 +22,52 @@ impl Command {
/// Execute `db account-storage` command
pub fn execute<N: NodeTypesWithDB>(self, tool: &DbTool<N>) -> eyre::Result<()> {
let address = self.address;
let use_hashed_state = tool.provider_factory.cached_storage_settings().use_hashed_state;
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();
let (slot_count, storage_size) = if use_hashed_state {
let hashed_address = keccak256(address);
tool.provider_factory.db_ref().view(|tx| {
let mut cursor = tx.cursor_dup_read::<tables::HashedStorages>()?;
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;
let walker = cursor.walk_dup(Some(hashed_address), None)?;
for entry in walker {
let (_, storage_entry) = entry?;
count += 1;
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 hashed storage slots"
);
last_log = Instant::now();
}
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();
}
}
let total_size = if count > 0 { 32 + total_value_bytes } else { 0 };
// 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))
})??
} else {
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();
Ok::<_, eyre::Report>((count, total_size))
})??;
// 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;
let mut buf = Vec::new();
// StorageEntry encodes as: 32 bytes (key/subkey uncompressed) + compressed U256
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}");
if use_hashed_state {
println!("Hashed storage size: {} (estimated)", human_bytes(storage_size as f64));
} else {
// Estimate hashed storage size: 32-byte B256 key instead of 20-byte Address
let hashed_size_estimate = if slot_count > 0 { storage_size + 12 } else { 0 };
let total_estimate = storage_size + hashed_size_estimate;
println!("Plain storage size: {} (estimated)", human_bytes(storage_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));
}
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(())
}

View File

@@ -98,8 +98,7 @@ impl Command {
)?;
if let Some(entry) = entry {
let se: reth_primitives_traits::StorageEntry = entry.into();
println!("{}", serde_json::to_string_pretty(&se)?);
println!("{}", serde_json::to_string_pretty(&entry)?);
} else {
error!(target: "reth::cli", "No content for the given table key.");
}
@@ -107,14 +106,7 @@ impl Command {
}
let changesets = provider.storage_changeset(key.block_number())?;
let serializable: Vec<_> = changesets
.into_iter()
.map(|(addr, entry)| {
let se: reth_primitives_traits::StorageEntry = entry.into();
(addr, se)
})
.collect();
println!("{}", serde_json::to_string_pretty(&serializable)?);
println!("{}", serde_json::to_string_pretty(&changesets)?);
return Ok(());
}

View File

@@ -1,4 +1,4 @@
use alloy_primitives::{keccak256, Address, BlockNumber, B256, U256};
use alloy_primitives::{Address, BlockNumber, B256, U256};
use clap::Parser;
use parking_lot::Mutex;
use reth_db_api::{
@@ -63,65 +63,39 @@ impl Command {
address: Address,
limit: usize,
) -> eyre::Result<()> {
let use_hashed_state = tool.provider_factory.cached_storage_settings().use_hashed_state;
let entries = tool.provider_factory.db_ref().view(|tx| {
let (account, walker_entries) = if use_hashed_state {
let hashed_address = keccak256(address);
let account = tx.get::<tables::HashedAccounts>(hashed_address)?;
let mut cursor = tx.cursor_dup_read::<tables::HashedStorages>()?;
let walker = cursor.walk_dup(Some(hashed_address), None)?;
let mut entries = Vec::new();
let mut last_log = Instant::now();
for (idx, entry) in walker.enumerate() {
let (_, storage_entry) = entry?;
if storage_entry.value != U256::ZERO {
entries.push((storage_entry.key, storage_entry.value));
}
if entries.len() >= limit {
break;
}
if last_log.elapsed() >= LOG_INTERVAL {
info!(
target: "reth::cli",
address = %address,
slots_scanned = idx,
"Scanning storage slots"
);
last_log = Instant::now();
}
}
(account, entries)
} else {
// Get account info
let account = tx.get::<tables::PlainAccountState>(address)?;
// Get storage entries
let mut cursor = tx.cursor_dup_read::<tables::PlainStorageState>()?;
let walker = cursor.walk_dup(Some(address), None)?;
let mut entries = Vec::new();
let mut last_log = Instant::now();
for (idx, entry) in walker.enumerate() {
let (_, storage_entry) = entry?;
if storage_entry.value != U256::ZERO {
entries.push((storage_entry.key, storage_entry.value));
}
if entries.len() >= limit {
break;
}
if last_log.elapsed() >= LOG_INTERVAL {
info!(
target: "reth::cli",
address = %address,
slots_scanned = idx,
"Scanning storage slots"
);
last_log = Instant::now();
}
}
(account, entries)
};
// Get account info
let account = tx.get::<tables::PlainAccountState>(address)?;
Ok::<_, eyre::Report>((account, walker_entries))
// Get storage entries
let mut cursor = tx.cursor_dup_read::<tables::PlainStorageState>()?;
let mut entries = Vec::new();
let mut last_log = Instant::now();
let walker = cursor.walk_dup(Some(address), None)?;
for (idx, entry) in walker.enumerate() {
let (_, storage_entry) = entry?;
if storage_entry.value != U256::ZERO {
entries.push((storage_entry.key, storage_entry.value));
}
if entries.len() >= limit {
break;
}
if last_log.elapsed() >= LOG_INTERVAL {
info!(
target: "reth::cli",
address = %address,
slots_scanned = idx,
"Scanning storage slots"
);
last_log = Instant::now();
}
}
Ok::<_, eyre::Report>((account, entries))
})??;
let (account, storage_entries) = entries;

View File

@@ -119,8 +119,8 @@ pub struct NodeCommand<C: ChainSpecParser, Ext: clap::Args + fmt::Debug = NoArgs
#[command(flatten, next_help_heading = "Static Files")]
pub static_files: StaticFilesArgs,
/// All storage related arguments with --storage prefix
#[command(flatten, next_help_heading = "Storage")]
/// Storage mode configuration (v2 vs v1/legacy)
#[command(flatten)]
pub storage: StorageArgs,
/// Additional cli arguments

View File

@@ -45,16 +45,12 @@ impl<C: ChainSpecParser> Command<C> {
let tool = DbTool::new(provider_factory)?;
let static_file_segments = match self.stage {
StageEnum::Headers => vec![StaticFileSegment::Headers],
StageEnum::Bodies => vec![StaticFileSegment::Transactions],
StageEnum::Execution => vec![
StaticFileSegment::Receipts,
StaticFileSegment::AccountChangeSets,
StaticFileSegment::StorageChangeSets,
],
StageEnum::Senders => vec![StaticFileSegment::TransactionSenders],
_ => vec![],
let static_file_segment = match self.stage {
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
@@ -62,33 +58,35 @@ impl<C: ChainSpecParser> Command<C> {
// 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.
let static_file_provider = tool.provider_factory.static_file_provider();
for segment in static_file_segments {
if let Some(highest_block) = static_file_provider.get_highest_static_file_block(segment)
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(segment)?;
let mut writer = static_file_provider.latest_writer(static_file_segment)?;
match 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(segment)
.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(segment)
.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(segment)
.get_highest_static_file_tx(static_file_segment)
.map(|tx_num| tx_num + 1)
.unwrap_or_default();
writer.prune_transaction_senders(to_delete, 0)?;
@@ -133,15 +131,8 @@ impl<C: ChainSpecParser> Command<C> {
reset_stage_checkpoint(tx, StageId::SenderRecovery)?;
}
StageEnum::Execution => {
if provider_rw.cached_storage_settings().use_hashed_state {
tx.clear::<tables::HashedAccounts>()?;
tx.clear::<tables::HashedStorages>()?;
reset_stage_checkpoint(tx, StageId::AccountHashing)?;
reset_stage_checkpoint(tx, StageId::StorageHashing)?;
} else {
tx.clear::<tables::PlainAccountState>()?;
tx.clear::<tables::PlainStorageState>()?;
}
tx.clear::<tables::PlainAccountState>()?;
tx.clear::<tables::PlainStorageState>()?;
tx.clear::<tables::AccountChangeSets>()?;
tx.clear::<tables::StorageChangeSets>()?;
tx.clear::<tables::Bytecodes>()?;

View File

@@ -1,6 +1,6 @@
//! Test setup utilities for configuring the initial state.
use crate::{testsuite::Environment, E2ETestSetupBuilder, NodeBuilderHelper};
use crate::{setup_engine_with_connection, testsuite::Environment, NodeBuilderHelper};
use alloy_eips::BlockNumberOrTag;
use alloy_primitives::B256;
use alloy_rpc_types_engine::{ForkchoiceState, PayloadAttributes};
@@ -38,8 +38,6 @@ pub struct Setup<I> {
shutdown_tx: Option<mpsc::Sender<()>>,
/// Is this setup in dev mode
pub is_dev: bool,
/// Whether to use v2 storage mode (hashed keys, static file changesets, rocksdb history)
pub storage_v2: bool,
/// Tracks instance generic.
_phantom: PhantomData<I>,
/// Holds the import result to keep nodes alive when using imported chain
@@ -60,7 +58,6 @@ impl<I> Default for Setup<I> {
tree_config: TreeConfig::default(),
shutdown_tx: None,
is_dev: true,
storage_v2: false,
_phantom: Default::default(),
import_result_holder: None,
import_rlp_path: None,
@@ -129,12 +126,6 @@ where
self
}
/// Enable v2 storage mode (hashed keys, static file changesets, rocksdb history)
pub const fn with_storage_v2(mut self) -> Self {
self.storage_v2 = true;
self
}
/// Apply setup using pre-imported chain data from RLP file
pub async fn apply_with_import<N>(
&mut self,
@@ -203,28 +194,19 @@ where
self.shutdown_tx = Some(shutdown_tx);
let is_dev = self.is_dev;
let storage_v2 = self.storage_v2;
let node_count = self.network.node_count;
let tree_config = self.tree_config.clone();
let attributes_generator = Self::create_static_attributes_generator::<N>();
let mut builder = E2ETestSetupBuilder::<N, _>::new(
let result = setup_engine_with_connection::<N>(
node_count,
Arc::<N::ChainSpec>::new((*chain_spec).clone().into()),
is_dev,
self.tree_config.clone(),
attributes_generator,
self.network.connect_nodes,
)
.with_tree_config_modifier(move |base| {
tree_config.clone().with_cross_block_cache_size(base.cross_block_cache_size())
})
.with_node_config_modifier(move |config| config.set_dev(is_dev))
.with_connect_nodes(self.network.connect_nodes);
if storage_v2 {
builder = builder.with_storage_v2();
}
let result = builder.build().await;
.await;
let mut node_clients = Vec::new();
match result {

View File

@@ -179,8 +179,6 @@ pub struct TreeConfig {
sparse_trie_prune_depth: usize,
/// Maximum number of storage tries to retain after pruning.
sparse_trie_max_storage_tries: usize,
/// Whether to fully disable sparse trie cache pruning between blocks.
disable_sparse_trie_cache_pruning: bool,
/// Timeout for the state root task before spawning a sequential fallback computation.
/// If `Some`, after waiting this duration for the state root task, a sequential state root
/// computation is spawned in parallel and whichever finishes first is used.
@@ -218,7 +216,6 @@ impl Default for TreeConfig {
disable_trie_cache: false,
sparse_trie_prune_depth: DEFAULT_SPARSE_TRIE_PRUNE_DEPTH,
sparse_trie_max_storage_tries: DEFAULT_SPARSE_TRIE_MAX_STORAGE_TRIES,
disable_sparse_trie_cache_pruning: false,
state_root_task_timeout: Some(DEFAULT_STATE_ROOT_TASK_TIMEOUT),
}
}
@@ -284,7 +281,6 @@ impl TreeConfig {
disable_trie_cache: false,
sparse_trie_prune_depth,
sparse_trie_max_storage_tries,
disable_sparse_trie_cache_pruning: false,
state_root_task_timeout,
}
}
@@ -635,17 +631,6 @@ impl TreeConfig {
self
}
/// Returns whether sparse trie cache pruning is disabled.
pub const fn disable_sparse_trie_cache_pruning(&self) -> bool {
self.disable_sparse_trie_cache_pruning
}
/// Setter for whether to disable sparse trie cache pruning.
pub const fn with_disable_sparse_trie_cache_pruning(mut self, value: bool) -> Self {
self.disable_sparse_trie_cache_pruning = value;
self
}
/// Returns the state root task timeout.
pub const fn state_root_task_timeout(&self) -> Option<Duration> {
self.state_root_task_timeout

View File

@@ -20,7 +20,7 @@ use reth_node_types::{BlockTy, NodeTypes};
use reth_payload_builder::PayloadBuilderHandle;
use reth_provider::{
providers::{BlockchainProvider, ProviderNodeTypes},
ProviderFactory, StorageSettingsCache,
ProviderFactory,
};
use reth_prune::PrunerWithFactory;
use reth_stages_api::{MetricEventsSender, Pipeline};
@@ -94,7 +94,6 @@ where
if chain_spec.is_optimism() { EngineApiKind::OpStack } else { EngineApiKind::Ethereum };
let downloader = BasicBlockDownloader::new(client, consensus.clone());
let use_hashed_state = provider.cached_storage_settings().use_hashed_state;
let persistence_handle =
PersistenceHandle::<N::Primitives>::spawn_service(provider, pruner, sync_metrics_tx);
@@ -112,7 +111,6 @@ where
engine_kind,
evm_config,
changeset_cache,
use_hashed_state,
);
let engine_handler = EngineApiRequestHandler::new(to_tree_tx, from_tree);

View File

@@ -143,13 +143,6 @@ test-utils = [
"reth-evm-ethereum/test-utils",
"reth-tasks/test-utils",
]
rocksdb = [
"reth-provider/rocksdb",
"reth-prune/rocksdb",
"reth-stages?/rocksdb",
"reth-e2e-test-utils/rocksdb",
]
edge = ["rocksdb"]
[[test]]
name = "e2e_testsuite"

View File

@@ -351,14 +351,6 @@ impl<S: StateProvider, const PREWARM: bool> StateProvider for CachedStateProvide
self.state_provider.storage(account, storage_key)
}
}
fn storage_by_hashed_key(
&self,
address: Address,
hashed_storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
self.state_provider.storage_by_hashed_key(address, hashed_storage_key)
}
}
impl<S: BytecodeReader, const PREWARM: bool> BytecodeReader for CachedStateProvider<S, PREWARM> {

View File

@@ -199,17 +199,6 @@ impl<S: StateProvider> StateProvider for InstrumentedStateProvider<S> {
self.record_storage_fetch(start.elapsed());
res
}
fn storage_by_hashed_key(
&self,
address: Address,
hashed_storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
let start = Instant::now();
let res = self.state_provider.storage_by_hashed_key(address, hashed_storage_key);
self.record_storage_fetch(start.elapsed());
res
}
}
impl<S: BytecodeReader> BytecodeReader for InstrumentedStateProvider<S> {

View File

@@ -12,13 +12,12 @@ use reth_primitives_traits::constants::gas_units::MEGAGAS;
use reth_trie::updates::TrieUpdates;
use std::time::{Duration, Instant};
/// Upper bounds for each gas bucket. The last bucket is a catch-all for
/// everything above the final threshold: <5M, 5-10M, 10-20M, 20-30M, 30-40M, >40M.
const GAS_BUCKET_THRESHOLDS: [u64; 5] =
[5 * MEGAGAS, 10 * MEGAGAS, 20 * MEGAGAS, 30 * MEGAGAS, 40 * MEGAGAS];
/// Width of each gas bucket in gas units (10 Mgas).
const GAS_BUCKET_SIZE: u64 = 10 * MEGAGAS;
/// Total number of gas buckets (thresholds + 1 catch-all).
const NUM_GAS_BUCKETS: usize = GAS_BUCKET_THRESHOLDS.len() + 1;
/// Number of gas buckets. The last bucket is a catch-all for everything above
/// `(NUM_GAS_BUCKETS - 1) * GAS_BUCKET_SIZE`.
const NUM_GAS_BUCKETS: usize = 5;
/// Metrics for the `EngineApi`.
#[derive(Debug, Default)]
@@ -281,23 +280,21 @@ impl GasBucketMetrics {
}
fn bucket_index(gas_used: u64) -> usize {
GAS_BUCKET_THRESHOLDS
.iter()
.position(|&threshold| gas_used < threshold)
.unwrap_or(GAS_BUCKET_THRESHOLDS.len())
let idx = gas_used / GAS_BUCKET_SIZE;
(idx as usize).min(NUM_GAS_BUCKETS - 1)
}
/// Returns a human-readable label like `<5M`, `5-10M`, … `>40M`.
/// Returns a human-readable label like `<10M`, `10-20M`, … `>40M`.
fn bucket_label(index: usize) -> String {
let m = GAS_BUCKET_SIZE / 1_000_000;
if index == 0 {
let hi = GAS_BUCKET_THRESHOLDS[0] / MEGAGAS;
format!("<{hi}M")
} else if index < GAS_BUCKET_THRESHOLDS.len() {
let lo = GAS_BUCKET_THRESHOLDS[index - 1] / MEGAGAS;
let hi = GAS_BUCKET_THRESHOLDS[index] / MEGAGAS;
format!("<{m}M")
} else if index < NUM_GAS_BUCKETS - 1 {
let lo = m * index as u64;
let hi = lo + m;
format!("{lo}-{hi}M")
} else {
let lo = GAS_BUCKET_THRESHOLDS[GAS_BUCKET_THRESHOLDS.len() - 1] / MEGAGAS;
let lo = m * index as u64;
format!(">{lo}M")
}
}

View File

@@ -32,7 +32,7 @@ use reth_provider::{
BlockExecutionOutput, BlockExecutionResult, BlockReader, ChangeSetReader,
DatabaseProviderFactory, HashedPostStateProvider, ProviderError, StageCheckpointReader,
StateProviderBox, StateProviderFactory, StateReader, StorageChangeSetReader,
StorageSettingsCache, TransactionVariant,
TransactionVariant,
};
use reth_revm::database::StateProviderDatabase;
use reth_stages_api::ControlFlow;
@@ -271,9 +271,6 @@ where
evm_config: C,
/// Changeset cache for in-memory trie changesets
changeset_cache: ChangesetCache,
/// Whether the node uses hashed state as canonical storage (v2 mode).
/// Cached at construction to avoid threading `StorageSettingsCache` bounds everywhere.
use_hashed_state: bool,
}
impl<N, P: Debug, T: PayloadTypes + Debug, V: Debug, C> std::fmt::Debug
@@ -299,7 +296,6 @@ where
.field("engine_kind", &self.engine_kind)
.field("evm_config", &self.evm_config)
.field("changeset_cache", &self.changeset_cache)
.field("use_hashed_state", &self.use_hashed_state)
.finish()
}
}
@@ -317,8 +313,7 @@ where
P::Provider: BlockReader<Block = N::Block, Header = N::BlockHeader>
+ StageCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache,
+ StorageChangeSetReader,
C: ConfigureEvm<Primitives = N> + 'static,
T: PayloadTypes<BuiltPayload: BuiltPayload<Primitives = N>>,
V: EngineValidator<T>,
@@ -339,7 +334,6 @@ where
engine_kind: EngineApiKind,
evm_config: C,
changeset_cache: ChangesetCache,
use_hashed_state: bool,
) -> Self {
let (incoming_tx, incoming) = crossbeam_channel::unbounded();
@@ -361,7 +355,6 @@ where
engine_kind,
evm_config,
changeset_cache,
use_hashed_state,
}
}
@@ -382,7 +375,6 @@ where
kind: EngineApiKind,
evm_config: C,
changeset_cache: ChangesetCache,
use_hashed_state: bool,
) -> (Sender<FromEngine<EngineApiRequest<T, N>, N::Block>>, UnboundedReceiver<EngineApiEvent<N>>)
{
let best_block_number = provider.best_block_number().unwrap_or(0);
@@ -415,7 +407,6 @@ where
kind,
evm_config,
changeset_cache,
use_hashed_state,
);
let incoming = task.incoming_tx.clone();
spawn_os_thread("engine", || task.run());
@@ -2388,12 +2379,7 @@ where
self.update_reorg_metrics(old.len(), old_first);
self.reinsert_reorged_blocks(new.clone());
// When use_hashed_state is enabled, skip reinserting the old chain — the
// bundle state references plain state reverts which don't exist.
if !self.use_hashed_state {
self.reinsert_reorged_blocks(old.clone());
}
self.reinsert_reorged_blocks(old.clone());
}
// update the tracked in-memory state with the new chain

View File

@@ -94,9 +94,6 @@ pub const SPARSE_TRIE_MAX_NODES_SHRINK_CAPACITY: usize = 1_000_000;
/// 144MB.
pub const SPARSE_TRIE_MAX_VALUES_SHRINK_CAPACITY: usize = 1_000_000;
/// Blocks with fewer transactions than this skip prewarming, since the fixed overhead of spawning
/// prewarm workers exceeds the execution time saved.
pub const SMALL_BLOCK_TX_THRESHOLD: usize = 5;
/// Type alias for [`PayloadHandle`] returned by payload processor spawn methods.
type IteratorPayloadHandle<Evm, I, N> = PayloadHandle<
WithTxEnv<TxEnvFor<Evm>, <I as ExecutableTxIterator<Evm>>::Recovered>,
@@ -138,8 +135,6 @@ where
sparse_trie_prune_depth: usize,
/// Maximum storage tries to retain after pruning.
sparse_trie_max_storage_tries: usize,
/// Whether sparse trie cache pruning is fully disabled.
disable_sparse_trie_cache_pruning: bool,
/// Whether to disable cache metrics recording.
disable_cache_metrics: bool,
}
@@ -175,7 +170,6 @@ where
prewarm_max_concurrency: config.prewarm_max_concurrency(),
sparse_trie_prune_depth: config.sparse_trie_prune_depth(),
sparse_trie_max_storage_tries: config.sparse_trie_max_storage_tries(),
disable_sparse_trie_cache_pruning: config.disable_sparse_trie_cache_pruning(),
disable_cache_metrics: config.disable_cache_metrics(),
}
}
@@ -472,8 +466,7 @@ where
where
P: BlockReader + StateProviderFactory + StateReader + Clone + 'static,
{
let skip_prewarm =
self.disable_transaction_prewarming || env.transaction_count < SMALL_BLOCK_TX_THRESHOLD;
let skip_prewarm = self.disable_transaction_prewarming;
let saved_cache = self.disable_state_cache.not().then(|| self.cache_for(env.parent_hash));
@@ -553,7 +546,6 @@ where
let disable_trie_cache = config.disable_trie_cache();
let prune_depth = self.sparse_trie_prune_depth;
let max_storage_tries = self.sparse_trie_max_storage_tries;
let disable_cache_pruning = self.disable_sparse_trie_cache_pruning;
let chunk_size =
config.multiproof_chunking_enabled().then_some(config.multiproof_chunk_size());
let executor = self.executor.clone();
@@ -650,7 +642,6 @@ where
max_storage_tries,
SPARSE_TRIE_MAX_NODES_SHRINK_CAPACITY,
SPARSE_TRIE_MAX_VALUES_SHRINK_CAPACITY,
disable_cache_pruning,
);
trie_metrics
.into_trie_for_reuse_duration_histogram

View File

@@ -1541,7 +1541,6 @@ mod tests {
providers::OverlayStateProviderFactory, test_utils::create_test_provider_factory,
BlockNumReader, BlockReader, ChangeSetReader, DatabaseProviderFactory, LatestStateProvider,
PruneCheckpointReader, StageCheckpointReader, StateProviderBox, StorageChangeSetReader,
StorageSettingsCache,
};
use reth_trie::MultiProof;
use reth_trie_db::ChangesetCache;
@@ -1563,7 +1562,6 @@ mod tests {
+ PruneCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ BlockNumReader,
> + Clone
+ Send
@@ -1583,10 +1581,7 @@ mod tests {
fn create_cached_provider<F>(factory: F) -> CachedStateProvider<StateProviderBox>
where
F: DatabaseProviderFactory<
Provider: BlockReader
+ StageCheckpointReader
+ PruneCheckpointReader
+ reth_provider::StorageSettingsCache,
Provider: BlockReader + StageCheckpointReader + PruneCheckpointReader,
> + Clone
+ Send
+ 'static,

View File

@@ -72,7 +72,6 @@ where
max_storage_tries: usize,
max_nodes_capacity: usize,
max_values_capacity: usize,
disable_pruning: bool,
) -> (SparseStateTrie<A, S>, DeferredDrops) {
match self {
Self::Cleared(task) => task.into_cleared_trie(max_nodes_capacity, max_values_capacity),
@@ -81,7 +80,6 @@ where
max_storage_tries,
max_nodes_capacity,
max_values_capacity,
disable_pruning,
),
}
}
@@ -358,23 +356,16 @@ where
/// Prunes and shrinks the trie for reuse in the next payload built on top of this one.
///
/// Should be called after the state root result has been sent.
///
/// When `disable_pruning` is true, the trie is preserved without any node pruning,
/// storage trie eviction, or capacity shrinking, keeping the full cache intact for
/// benchmarking purposes.
pub(super) fn into_trie_for_reuse(
self,
prune_depth: usize,
max_storage_tries: usize,
max_nodes_capacity: usize,
max_values_capacity: usize,
disable_pruning: bool,
) -> (SparseStateTrie<A, S>, DeferredDrops) {
let Self { mut trie, .. } = self;
if !disable_pruning {
trie.prune(prune_depth, max_storage_tries);
trie.shrink_to(max_nodes_capacity, max_values_capacity);
}
trie.prune(prune_depth, max_storage_tries);
trie.shrink_to(max_nodes_capacity, max_values_capacity);
let deferred = trie.take_deferred_drops();
(trie, deferred)
}

View File

@@ -38,7 +38,7 @@ use reth_provider::{
providers::OverlayStateProviderFactory, BlockExecutionOutput, BlockNumReader, BlockReader,
ChangeSetReader, DatabaseProviderFactory, DatabaseProviderROFactory, HashedPostStateProvider,
ProviderError, PruneCheckpointReader, StageCheckpointReader, StateProvider,
StateProviderFactory, StateReader, StorageChangeSetReader, StorageSettingsCache,
StateProviderFactory, StateReader, StorageChangeSetReader,
};
use reth_revm::db::{states::bundle_state::BundleRetention, State};
use reth_trie::{updates::TrieUpdates, HashedPostState, StateRoot};
@@ -146,8 +146,7 @@ where
+ PruneCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ StorageSettingsCache,
+ BlockNumReader,
> + BlockReader<Header = N::BlockHeader>
+ ChangeSetReader
+ BlockNumReader
@@ -1527,8 +1526,7 @@ where
+ PruneCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ StorageSettingsCache,
+ BlockNumReader,
> + BlockReader<Header = N::BlockHeader>
+ StateProviderFactory
+ StateReader

View File

@@ -221,7 +221,6 @@ impl TestHarness {
EngineApiKind::Ethereum,
evm_config,
changeset_cache,
provider.cached_storage_settings().use_hashed_state,
);
let block_builder = TestBlockBuilder::default().with_chain_spec((*chain_spec).clone());

View File

@@ -2,15 +2,13 @@
mod fcu_finalized_blocks;
use alloy_rpc_types_engine::PayloadStatusEnum;
use eyre::Result;
use reth_chainspec::{ChainSpecBuilder, MAINNET};
use reth_e2e_test_utils::testsuite::{
actions::{
BlockReference, CaptureBlock, CompareNodeChainTips, CreateFork, ExpectFcuStatus,
MakeCanonical, ProduceBlocks, ProduceBlocksLocally, ProduceInvalidBlocks, ReorgTo,
SelectActiveNode, SendForkchoiceUpdate, SendNewPayloads, SetForkBase, UpdateBlockInfo,
ValidateCanonicalTag, WaitForSync,
CaptureBlock, CompareNodeChainTips, CreateFork, ExpectFcuStatus, MakeCanonical,
ProduceBlocks, ProduceBlocksLocally, ProduceInvalidBlocks, ReorgTo, SelectActiveNode,
SendNewPayloads, UpdateBlockInfo, ValidateCanonicalTag, WaitForSync,
},
setup::{NetworkSetup, Setup},
TestBuilder,
@@ -41,14 +39,6 @@ fn default_engine_tree_setup() -> Setup<EthEngineTypes> {
)
}
/// Creates a v2 storage mode setup for engine tree e2e tests.
///
/// v2 mode uses keccak256-hashed slot keys in static file changesets and rocksdb history
/// instead of plain keys in MDBX.
fn v2_engine_tree_setup() -> Setup<EthEngineTypes> {
default_engine_tree_setup().with_storage_v2()
}
/// Test that verifies forkchoice update and canonical chain insertion functionality.
#[tokio::test]
async fn test_engine_tree_fcu_canon_chain_insertion_e2e() -> Result<()> {
@@ -344,152 +334,3 @@ async fn test_engine_tree_live_sync_transition_eventually_canonical_e2e() -> Res
Ok(())
}
// ==================== v2 storage mode variants ====================
/// v2 variant: Verifies forkchoice update and canonical chain insertion in v2 storage mode.
///
/// Exercises the full `save_blocks` → `write_state` → static file changeset path with hashed keys.
#[tokio::test]
async fn test_engine_tree_fcu_canon_chain_insertion_v2_e2e() -> Result<()> {
reth_tracing::init_test_tracing();
let test = TestBuilder::new()
.with_setup(v2_engine_tree_setup())
.with_action(ProduceBlocks::<EthEngineTypes>::new(1))
.with_action(MakeCanonical::new())
.with_action(ProduceBlocks::<EthEngineTypes>::new(3))
.with_action(MakeCanonical::new());
test.run::<EthereumNode>().await?;
Ok(())
}
/// v2 variant: Verifies forkchoice update with a reorg where all blocks are already available.
///
/// Exercises `write_state_reverts` path with hashed changeset keys during CL-driven reorgs.
#[tokio::test]
async fn test_engine_tree_fcu_reorg_with_all_blocks_v2_e2e() -> Result<()> {
reth_tracing::init_test_tracing();
let test = TestBuilder::new()
.with_setup(v2_engine_tree_setup())
.with_action(ProduceBlocks::<EthEngineTypes>::new(5))
.with_action(MakeCanonical::new())
.with_action(CreateFork::<EthEngineTypes>::new(2, 3))
.with_action(CaptureBlock::new("fork_tip"))
.with_action(ReorgTo::<EthEngineTypes>::new_from_tag("fork_tip"));
test.run::<EthereumNode>().await?;
Ok(())
}
/// v2 variant: Verifies progressive canonical chain extension in v2 storage mode.
#[tokio::test]
async fn test_engine_tree_fcu_extends_canon_chain_v2_e2e() -> Result<()> {
reth_tracing::init_test_tracing();
let test = TestBuilder::new()
.with_setup(v2_engine_tree_setup())
.with_action(ProduceBlocks::<EthEngineTypes>::new(1))
.with_action(MakeCanonical::new())
.with_action(ProduceBlocks::<EthEngineTypes>::new(10))
.with_action(CaptureBlock::new("target_block"))
.with_action(ReorgTo::<EthEngineTypes>::new_from_tag("target_block"))
.with_action(MakeCanonical::new());
test.run::<EthereumNode>().await?;
Ok(())
}
/// Creates a 2-node setup for disk-level reorg testing.
///
/// Uses unconnected nodes so fork blocks can be produced independently on Node 1 and then
/// sent to Node 0 via newPayload only (no FCU), keeping Node 0's persisted chain intact
/// until the final `ReorgTo` triggers `find_disk_reorg`.
fn disk_reorg_setup(storage_v2: bool) -> Setup<EthEngineTypes> {
let mut setup = Setup::default()
.with_chain_spec(Arc::new(
ChainSpecBuilder::default()
.chain(MAINNET.chain)
.genesis(
serde_json::from_str(include_str!(
"../../../../e2e-test-utils/src/testsuite/assets/genesis.json"
))
.unwrap(),
)
.cancun_activated()
.build(),
))
.with_network(NetworkSetup::multi_node_unconnected(2))
.with_tree_config(
TreeConfig::default().with_legacy_state_root(false).with_has_enough_parallelism(true),
);
if storage_v2 {
setup = setup.with_storage_v2();
}
setup
}
/// Builds a disk-level reorg test scenario.
///
/// 1. Both nodes receive 3 shared blocks
/// 2. Node 0 extends to 10 blocks locally (persisted to disk)
/// 3. Node 1 builds an 8-block fork from block 3 (its canonical head)
/// 4. Fork blocks are sent to Node 0 via newPayload (no FCU, old chain stays on disk)
/// 5. FCU to fork tip on Node 0 triggers `find_disk_reorg` → `RemoveBlocksAbove(3)`
fn disk_reorg_test(storage_v2: bool) -> TestBuilder<EthEngineTypes> {
TestBuilder::new()
.with_setup(disk_reorg_setup(storage_v2))
.with_action(SelectActiveNode::new(0))
.with_action(ProduceBlocks::<EthEngineTypes>::new(3))
.with_action(MakeCanonical::new())
.with_action(ProduceBlocksLocally::<EthEngineTypes>::new(7))
.with_action(MakeCanonical::with_active_node())
.with_action(SelectActiveNode::new(1))
.with_action(SetForkBase::new(3))
.with_action(ProduceBlocksLocally::<EthEngineTypes>::new(8))
.with_action(MakeCanonical::with_active_node())
.with_action(CaptureBlock::new("fork_tip"))
.with_action(
SendNewPayloads::<EthEngineTypes>::new()
.with_source_node(1)
.with_target_node(0)
.with_start_block(4)
.with_total_blocks(8),
)
.with_action(
SendForkchoiceUpdate::<EthEngineTypes>::new(
BlockReference::Tag("fork_tip".into()),
BlockReference::Tag("fork_tip".into()),
BlockReference::Tag("fork_tip".into()),
)
.with_expected_status(PayloadStatusEnum::Valid)
.with_node_idx(0),
)
}
/// Verifies disk-level reorg in v1 (plain key) storage mode.
///
/// Confirms `find_disk_reorg()` detects persisted blocks on the wrong fork and calls
/// `RemoveBlocksAbove` to truncate, then re-persists the correct fork chain.
#[tokio::test]
async fn test_engine_tree_disk_reorg_v1_e2e() -> Result<()> {
reth_tracing::init_test_tracing();
disk_reorg_test(false).run::<EthereumNode>().await?;
Ok(())
}
/// v2 variant: Verifies disk-level reorg in v2 storage mode.
///
/// Same scenario as v1 but with hashed changeset keys in static files and rocksdb history.
/// Exercises `find_disk_reorg()` → `RemoveBlocksAbove` with v2 hashed key format.
#[tokio::test]
async fn test_engine_tree_disk_reorg_v2_e2e() -> Result<()> {
reth_tracing::init_test_tracing();
disk_reorg_test(true).run::<EthereumNode>().await?;
Ok(())
}

View File

@@ -273,6 +273,7 @@ where
gas_limit: payload.payload.gas_limit(),
basefee: payload.payload.saturated_base_fee_per_gas(),
blob_excess_gas_and_price,
slot_num: 0,
};
Ok(EvmEnv { cfg_env, block_env })

View File

@@ -108,8 +108,7 @@ impl<'a, DB: Database, I: Inspector<EthEvmContext<&'a mut State<DB>>>> BlockExec
result: ResultAndState::new(
ExecutionResult::Success {
reason: SuccessReason::Return,
gas_used: 0,
gas_refunded: 0,
gas: Default::default(),
logs: vec![],
output: Output::Call(Bytes::from(vec![])),
},

View File

@@ -208,9 +208,9 @@ impl<T> ExecutionOutcome<T> {
}
/// Returns [`HashedPostState`] for this execution outcome.
/// See [`HashedPostState::from_bundle_state_adaptive`] for more info.
/// See [`HashedPostState::from_bundle_state`] for more info.
pub fn hash_state_slow<KH: KeyHasher>(&self) -> HashedPostState {
HashedPostState::from_bundle_state_adaptive::<KH>(&self.bundle.state)
HashedPostState::from_bundle_state::<KH>(&self.bundle.state)
}
/// Transform block number to the index of block.

View File

@@ -18,7 +18,6 @@ use reth_provider::{
};
use reth_revm::database::StateProviderDatabase;
use reth_testing_utils::generators::sign_tx_with_key_pair;
use reth_trie_common::KeccakKeyHasher;
use secp256k1::Keypair;
pub(crate) fn to_execution_outcome(
@@ -78,9 +77,12 @@ where
let execution_outcome = to_execution_outcome(block.number(), &block_execution_output);
// Commit the block's execution outcome to the database
let hashed_state = execution_outcome.hash_state_slow::<KeccakKeyHasher>().into_sorted();
let provider_rw = provider_factory.provider_rw()?;
provider_rw.append_blocks_with_state(vec![block.clone()], &execution_outcome, hashed_state)?;
provider_rw.append_blocks_with_state(
vec![block.clone()],
&execution_outcome,
Default::default(),
)?;
provider_rw.commit()?;
Ok(block_execution_output)
@@ -208,12 +210,11 @@ where
execution_outcome.state_mut().reverts.sort();
// Commit the block's execution outcome to the database
let hashed_state = execution_outcome.hash_state_slow::<KeccakKeyHasher>().into_sorted();
let provider_rw = provider_factory.provider_rw()?;
provider_rw.append_blocks_with_state(
vec![block1.clone(), block2.clone()],
&execution_outcome,
hashed_state,
Default::default(),
)?;
provider_rw.commit()?;

View File

@@ -589,7 +589,7 @@ impl EthMessageID {
/// Returns the max value for the given version.
pub const fn max(version: EthVersion) -> u8 {
if version as u8 >= EthVersion::Eth69 as u8 {
if version.is_eth69() {
Self::BlockRangeUpdate.to_u8()
} else {
Self::Receipts.to_u8()
@@ -937,13 +937,6 @@ mod tests {
assert!(matches!(decoded, StatusMessage::Legacy(s) if s == status));
}
#[test]
fn eth_message_id_max_includes_block_range_update() {
assert_eq!(EthMessageID::max(EthVersion::Eth69), EthMessageID::BlockRangeUpdate.to_u8(),);
assert_eq!(EthMessageID::max(EthVersion::Eth70), EthMessageID::BlockRangeUpdate.to_u8(),);
assert_eq!(EthMessageID::max(EthVersion::Eth68), EthMessageID::Receipts.to_u8());
}
#[test]
fn decode_status_rejects_non_status() {
let msg = EthMessage::<EthNetworkPrimitives>::GetBlockBodies(RequestPair {

View File

@@ -8,7 +8,7 @@ use reth_eth_wire_types::{
};
use reth_ethereum_forks::ForkId;
use reth_network_p2p::error::{RequestError, RequestResult};
use reth_network_peers::{NodeRecord, PeerId};
use reth_network_peers::PeerId;
use reth_network_types::{PeerAddr, PeerKind};
use reth_tokio_util::EventStream;
use std::{
@@ -152,13 +152,8 @@ pub trait NetworkEventListenerProvider: NetworkPeersEvents {
pub enum DiscoveryEvent {
/// Discovered a node
NewNode(DiscoveredEvent),
/// Retrieved a [`ForkId`] from the peer via ENR request.
///
/// Contains the full [`NodeRecord`] (peer ID + address) and the reported [`ForkId`].
/// Used to verify fork compatibility before admitting the peer.
///
/// See also <https://eips.ethereum.org/EIPS/eip-868>
EnrForkId(NodeRecord, ForkId),
/// Retrieved a [`ForkId`] from the peer via ENR request, See <https://eips.ethereum.org/EIPS/eip-868>
EnrForkId(PeerId, ForkId),
}
/// Represents events related to peer discovery in the network.

View File

@@ -172,11 +172,6 @@ pub struct PeersConfig {
/// IPs within the specified CIDR ranges will be allowed.
#[cfg_attr(feature = "serde", serde(skip))]
pub ip_filter: IpFilter,
/// If true, discovered peers without a confirmed ENR [`ForkId`](alloy_eip2124::ForkId)
/// (EIP-868) will not be added to the peer set until their fork ID is verified.
///
/// This filters out peers from other networks that pollute the discovery table.
pub enforce_enr_fork_id: bool,
}
impl Default for PeersConfig {
@@ -196,7 +191,6 @@ impl Default for PeersConfig {
max_backoff_count: 5,
incoming_ip_throttle_duration: INBOUND_IP_THROTTLE_DURATION,
ip_filter: IpFilter::default(),
enforce_enr_fork_id: false,
}
}
}
@@ -320,13 +314,6 @@ impl PeersConfig {
self
}
/// If set, discovered peers without a confirmed ENR [`ForkId`](alloy_eip2124::ForkId) will not
/// be added to the peer set until their fork ID is verified via EIP-868.
pub const fn with_enforce_enr_fork_id(mut self, enforce: bool) -> Self {
self.enforce_enr_fork_id = enforce;
self
}
/// Returns settings for testing
#[cfg(any(test, feature = "test-utils"))]
pub fn test() -> Self {

View File

@@ -240,7 +240,7 @@ impl Discovery {
self.on_node_record_update(record, None);
}
DiscoveryUpdate::EnrForkId(node, fork_id) => {
self.queued_events.push_back(DiscoveryEvent::EnrForkId(node, fork_id))
self.queued_events.push_back(DiscoveryEvent::EnrForkId(node.id, fork_id))
}
DiscoveryUpdate::Removed(peer_id) => {
self.discovered_nodes.remove(&peer_id);

View File

@@ -92,9 +92,6 @@ pub struct PeersManager {
incoming_ip_throttle_duration: Duration,
/// IP address filter for restricting network connections to specific IP ranges.
ip_filter: reth_net_banlist::IpFilter,
/// If true, discovered peers without a confirmed ENR fork ID will not be added until their
/// fork ID is verified via EIP-868.
enforce_enr_fork_id: bool,
}
impl PeersManager {
@@ -114,7 +111,6 @@ impl PeersManager {
max_backoff_count,
incoming_ip_throttle_duration,
ip_filter,
enforce_enr_fork_id,
} = config;
let (manager_tx, handle_rx) = mpsc::unbounded_channel();
let now = Instant::now();
@@ -171,7 +167,6 @@ impl PeersManager {
net_connection_state: NetworkConnectionState::default(),
incoming_ip_throttle_duration,
ip_filter,
enforce_enr_fork_id,
}
}
@@ -180,11 +175,6 @@ impl PeersManager {
PeersHandle::new(self.manager_tx.clone())
}
/// Returns `true` if discovered peers must have a confirmed ENR fork ID before being added.
pub(crate) const fn enforce_enr_fork_id(&self) -> bool {
self.enforce_enr_fork_id
}
/// Returns the number of peers in the peer set
#[inline]
pub(crate) fn num_known_peers(&self) -> usize {
@@ -748,6 +738,17 @@ impl PeersManager {
}
}
/// Called as follow-up for a discovered peer.
///
/// The [`ForkId`] is retrieved from an ENR record that the peer announces over the discovery
/// protocol
pub(crate) fn set_discovered_fork_id(&mut self, peer_id: PeerId, fork_id: ForkId) {
if let Some(peer) = self.peers.get_mut(&peer_id) {
trace!(target: "net::peers", ?peer_id, ?fork_id, "set discovered fork id");
peer.fork_id = Some(Box::new(fork_id));
}
}
/// Called for a newly discovered peer.
///
/// If the peer already exists, then the address, kind and `fork_id` will be updated.

View File

@@ -332,19 +332,9 @@ impl<N: NetworkPrimitives> NetworkState<N> {
fork_id,
});
}
DiscoveryEvent::EnrForkId(record, fork_id) => {
let peer_id = record.id;
let tcp_addr = record.tcp_addr();
if tcp_addr.port() == 0 {
return
}
let udp_addr = record.udp_addr();
let addr = PeerAddr::new(tcp_addr, Some(udp_addr));
self.queued_messages.push_back(StateAction::DiscoveredEnrForkId {
peer_id,
addr,
fork_id,
});
DiscoveryEvent::EnrForkId(peer_id, fork_id) => {
self.queued_messages
.push_back(StateAction::DiscoveredEnrForkId { peer_id, fork_id });
}
}
}
@@ -562,8 +552,6 @@ pub(crate) enum StateAction<N: NetworkPrimitives> {
/// Retrieved a [`ForkId`] from the peer via ENR request, See <https://eips.ethereum.org/EIPS/eip-868>
DiscoveredEnrForkId {
peer_id: PeerId,
/// The address of the peer.
addr: PeerAddr,
/// The reported [`ForkId`] by this peer.
fork_id: ForkId,
},

View File

@@ -246,28 +246,18 @@ impl<N: NetworkPrimitives> Swarm<N> {
StateAction::PeerAdded(peer_id) => return Some(SwarmEvent::PeerAdded(peer_id)),
StateAction::PeerRemoved(peer_id) => return Some(SwarmEvent::PeerRemoved(peer_id)),
StateAction::DiscoveredNode { peer_id, addr, fork_id } => {
// Don't try to connect to peer if node is shutting down
if self.is_shutting_down() {
return None
}
// When `enforce_enr_fork_id` is enabled, peers discovered without a confirmed
// fork ID (via EIP-868 ENR) are deferred — they'll only be added once a
// `DiscoveredEnrForkId` event arrives with a validated fork ID.
//
// When disabled (default), peers without a fork ID are admitted immediately.
// Peers that *do* carry a fork ID are always validated against ours.
let enforce = self.state().peers().enforce_enr_fork_id();
let allow = match fork_id {
Some(f) => self.sessions.is_valid_fork_id(f),
None => !enforce,
};
if allow {
// Insert peer only if no fork id or a valid fork id
if fork_id.map_or_else(|| true, |f| self.sessions.is_valid_fork_id(f)) {
self.state_mut().peers_mut().add_peer(peer_id, addr, fork_id);
}
}
StateAction::DiscoveredEnrForkId { peer_id, addr, fork_id } => {
StateAction::DiscoveredEnrForkId { peer_id, fork_id } => {
if self.sessions.is_valid_fork_id(fork_id) {
self.state_mut().peers_mut().add_peer(peer_id, addr, Some(fork_id));
self.state_mut().peers_mut().set_discovered_fork_id(peer_id, fork_id);
} else {
trace!(target: "net", ?peer_id, remote_fork_id=?fork_id, our_fork_id=?self.sessions.fork_id(), "fork id mismatch, removing peer");
self.state_mut().peers_mut().remove_peer(peer_id);

View File

@@ -43,7 +43,6 @@ pub struct DefaultEngineValues {
disable_trie_cache: bool,
sparse_trie_prune_depth: usize,
sparse_trie_max_storage_tries: usize,
disable_sparse_trie_cache_pruning: bool,
state_root_task_timeout: Option<String>,
}
@@ -199,12 +198,6 @@ impl DefaultEngineValues {
self
}
/// Set whether to disable sparse trie cache pruning by default
pub const fn with_disable_sparse_trie_cache_pruning(mut self, v: bool) -> Self {
self.disable_sparse_trie_cache_pruning = v;
self
}
/// Set the default state root task timeout
pub fn with_state_root_task_timeout(mut self, v: Option<String>) -> Self {
self.state_root_task_timeout = v;
@@ -238,7 +231,6 @@ impl Default for DefaultEngineValues {
disable_trie_cache: false,
sparse_trie_prune_depth: DEFAULT_SPARSE_TRIE_PRUNE_DEPTH,
sparse_trie_max_storage_tries: DEFAULT_SPARSE_TRIE_MAX_STORAGE_TRIES,
disable_sparse_trie_cache_pruning: false,
state_root_task_timeout: Some("1s".to_string()),
}
}
@@ -380,12 +372,6 @@ pub struct EngineArgs {
#[arg(long = "engine.sparse-trie-max-storage-tries", default_value_t = DefaultEngineValues::get_global().sparse_trie_max_storage_tries)]
pub sparse_trie_max_storage_tries: usize,
/// Fully disable sparse trie cache pruning. When set, the cached sparse trie is preserved
/// without any node pruning or storage trie eviction between blocks. Useful for benchmarking
/// the effects of retaining the full trie cache.
#[arg(long = "engine.disable-sparse-trie-cache-pruning", default_value_t = DefaultEngineValues::get_global().disable_sparse_trie_cache_pruning)]
pub disable_sparse_trie_cache_pruning: bool,
/// Configure the timeout for the state root task before spawning a sequential fallback.
/// If the state root task takes longer than this, a sequential computation starts in
/// parallel and whichever finishes first is used.
@@ -429,7 +415,6 @@ impl Default for EngineArgs {
disable_trie_cache,
sparse_trie_prune_depth,
sparse_trie_max_storage_tries,
disable_sparse_trie_cache_pruning,
state_root_task_timeout,
} = DefaultEngineValues::get_global().clone();
Self {
@@ -460,7 +445,6 @@ impl Default for EngineArgs {
disable_trie_cache,
sparse_trie_prune_depth,
sparse_trie_max_storage_tries,
disable_sparse_trie_cache_pruning,
state_root_task_timeout: state_root_task_timeout
.as_deref()
.map(|s| humantime::parse_duration(s).expect("valid default duration")),
@@ -496,7 +480,6 @@ impl EngineArgs {
.with_disable_trie_cache(self.disable_trie_cache)
.with_sparse_trie_prune_depth(self.sparse_trie_prune_depth)
.with_sparse_trie_max_storage_tries(self.sparse_trie_max_storage_tries)
.with_disable_sparse_trie_cache_pruning(self.disable_sparse_trie_cache_pruning)
.with_state_root_task_timeout(self.state_root_task_timeout.filter(|d| !d.is_zero()))
}
}
@@ -551,7 +534,6 @@ mod tests {
disable_trie_cache: true,
sparse_trie_prune_depth: 10,
sparse_trie_max_storage_tries: 100,
disable_sparse_trie_cache_pruning: true,
state_root_task_timeout: Some(Duration::from_secs(2)),
};
@@ -588,7 +570,6 @@ mod tests {
"10",
"--engine.sparse-trie-max-storage-tries",
"100",
"--engine.disable-sparse-trie-cache-pruning",
"--engine.state-root-task-timeout",
"2s",
])

View File

@@ -80,7 +80,7 @@ pub use static_files::{StaticFilesArgs, MINIMAL_BLOCKS_PER_FILE};
mod rocksdb;
pub use rocksdb::{RocksDbArgs, RocksDbArgsError};
/// `StorageArgs` for configuring storage settings.
/// `StorageArgs` for configuring storage mode (v2 vs v1/legacy).
mod storage;
pub use storage::StorageArgs;

View File

@@ -227,14 +227,6 @@ pub struct NetworkArgs {
/// Example: --netrestrict "192.168.0.0/16,10.0.0.0/8"
#[arg(long, value_name = "NETRESTRICT")]
pub netrestrict: Option<String>,
/// Enforce EIP-868 ENR fork ID validation for discovered peers.
///
/// When enabled, peers discovered without a confirmed fork ID are not added to the peer set
/// until their fork ID is verified via EIP-868 ENR request. This filters out peers from other
/// networks that pollute the discovery table.
#[arg(long)]
pub enforce_enr_fork_id: bool,
}
impl NetworkArgs {
@@ -341,8 +333,7 @@ impl NetworkArgs {
)
.with_max_inbound_opt(self.resolved_max_inbound_peers())
.with_max_outbound_opt(self.resolved_max_outbound_peers())
.with_ip_filter(ip_filter)
.with_enforce_enr_fork_id(self.enforce_enr_fork_id);
.with_ip_filter(ip_filter);
// Configure basic network stack
NetworkConfigBuilder::<N>::new(secret_key)
@@ -500,7 +491,6 @@ impl Default for NetworkArgs {
required_block_hashes: vec![],
network_id: None,
netrestrict: None,
enforce_enr_fork_id: false,
}
}
}

View File

@@ -1,13 +1,11 @@
//! clap [Args](clap::Args) for storage configuration
//! clap [Args](clap::Args) for storage mode configuration
use clap::{ArgAction, Args};
/// Parameters for storage configuration.
/// Parameters for storage mode configuration.
///
/// This controls whether the node uses v2 storage defaults (with `RocksDB` and static file
/// optimizations) or v1/legacy storage defaults.
///
/// Individual storage settings can be overridden with `--static-files.*` and `--rocksdb.*` flags.
#[derive(Debug, Args, PartialEq, Eq, Clone, Copy, Default)]
#[command(next_help_heading = "Storage")]
pub struct StorageArgs {
@@ -42,24 +40,21 @@ mod tests {
use super::*;
use clap::Parser;
/// A helper type to parse Args more easily
#[derive(Parser)]
struct CommandParser<T: Args> {
struct CommandParser {
#[command(flatten)]
args: T,
args: StorageArgs,
}
#[test]
fn test_default_storage_args() {
let default_args = StorageArgs::default();
let args = CommandParser::<StorageArgs>::parse_from(["reth"]).args;
assert_eq!(args, default_args);
let args = CommandParser::parse_from(["reth"]).args;
assert!(!args.v2);
}
#[test]
fn test_parse_v2_flag() {
let args = CommandParser::<StorageArgs>::parse_from(["reth", "--storage.v2"]).args;
let args = CommandParser::parse_from(["reth", "--storage.v2"]).args;
assert!(args.v2);
}
}

View File

@@ -155,7 +155,7 @@ pub struct NodeConfig<ChainSpec> {
/// All `RocksDB` table routing arguments
pub rocksdb: RocksDbArgs,
/// All storage related arguments with --storage prefix
/// Storage mode configuration (v2 vs v1/legacy)
pub storage: StorageArgs,
}
@@ -355,12 +355,6 @@ impl<ChainSpec> NodeConfig<ChainSpec> {
self
}
/// Set the storage args for the node
pub const fn with_storage(mut self, storage: StorageArgs) -> Self {
self.storage = storage;
self
}
/// Returns pruning configuration.
pub fn prune_config(&self) -> Option<PruneConfig>
where
@@ -404,13 +398,6 @@ impl<ChainSpec> NodeConfig<ChainSpec> {
s = s.with_use_hashed_state(self.storage.use_hashed_state);
if s.use_hashed_state {
s = s.with_storage_changesets_in_static_files(true);
}
if s.storage_changesets_in_static_files {
s = s.with_use_hashed_state(true);
}
s
}

View File

@@ -265,7 +265,7 @@ impl NodeState {
warn!(number=block.number(), hash=?block.hash(), "Encountered invalid block");
}
ConsensusEngineEvent::BlockReceived(num_hash) => {
info!(number=num_hash.number, hash=?num_hash.hash, "Received new payload from consensus engine");
info!(number=num_hash.number, hash=?num_hash.hash, "Received block from consensus engine");
}
}
}

View File

@@ -4,7 +4,7 @@ use alloy_genesis::GenesisAccount;
use alloy_primitives::{keccak256, Bytes, B256, U256};
use alloy_trie::TrieAccount;
use derive_more::Deref;
use revm_bytecode::{Bytecode as RevmBytecode, BytecodeDecodeError};
use revm_bytecode::{Bytecode as RevmBytecode, BytecodeDecodeError, BytecodeKind};
use revm_state::AccountInfo;
#[cfg(any(test, feature = "reth-codec"))]
@@ -16,10 +16,10 @@ pub mod compact_ids {
/// Identifier for removed bytecode variant.
pub const REMOVED_BYTECODE_ID: u8 = 1;
/// Identifier for [`LegacyAnalyzed`](revm_bytecode::Bytecode::LegacyAnalyzed).
/// Identifier for [`LegacyAnalyzed`](revm_bytecode::BytecodeKind::LegacyAnalyzed).
pub const LEGACY_ANALYZED_BYTECODE_ID: u8 = 2;
/// Identifier for [`Eip7702`](revm_bytecode::Bytecode::Eip7702).
/// Identifier for [`Eip7702`](revm_bytecode::BytecodeKind::Eip7702).
pub const EIP7702_BYTECODE_ID: u8 = 4;
}
@@ -137,22 +137,19 @@ impl reth_codecs::Compact for Bytecode {
{
use compact_ids::{EIP7702_BYTECODE_ID, LEGACY_ANALYZED_BYTECODE_ID};
let bytecode = match &self.0 {
RevmBytecode::LegacyAnalyzed(analyzed) => analyzed.bytecode(),
RevmBytecode::Eip7702(eip7702) => eip7702.raw(),
};
let bytecode = self.0.bytecode();
buf.put_u32(bytecode.len() as u32);
buf.put_slice(bytecode.as_ref());
let len = match &self.0 {
let len = match self.0.kind() {
// [`REMOVED_BYTECODE_ID`] has been removed.
RevmBytecode::LegacyAnalyzed(analyzed) => {
BytecodeKind::LegacyAnalyzed => {
buf.put_u8(LEGACY_ANALYZED_BYTECODE_ID);
buf.put_u64(analyzed.original_len() as u64);
let map = analyzed.jump_table().as_slice();
buf.put_u64(self.0.len() as u64);
let map = self.0.legacy_jump_table().unwrap().as_slice();
buf.put_slice(map);
1 + 8 + map.len()
}
RevmBytecode::Eip7702(_) => {
BytecodeKind::Eip7702 => {
buf.put_u8(EIP7702_BYTECODE_ID);
1
}
@@ -251,12 +248,10 @@ impl From<Account> for AccountInfo {
#[cfg(test)]
mod tests {
use std::sync::Arc;
use super::*;
use alloy_primitives::{hex_literal::hex, B256, U256};
use reth_codecs::Compact;
use revm_bytecode::{JumpTable, LegacyAnalyzedBytecode};
use revm_bytecode::JumpTable;
#[test]
fn test_account() {
@@ -313,12 +308,11 @@ mod tests {
assert_eq!(len, 17);
let mut buf = vec![];
let bytecode =
Bytecode(RevmBytecode::LegacyAnalyzed(Arc::new(LegacyAnalyzedBytecode::new(
Bytes::from(&hex!("ff00")),
2,
JumpTable::from_slice(&[0], 2),
))));
let bytecode = Bytecode(RevmBytecode::new_analyzed(
Bytes::from(&hex!("ff00")),
2,
JumpTable::from_slice(&[0], 2),
));
let len = bytecode.to_compact(&mut buf);
assert_eq!(len, 16);

View File

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

View File

@@ -1,4 +1,4 @@
use alloy_primitives::{keccak256, B256, U256};
use alloy_primitives::{B256, U256};
/// Trait for `DupSort` table values that contain a subkey.
///
@@ -12,117 +12,6 @@ pub trait ValueWithSubKey {
fn get_subkey(&self) -> Self::SubKey;
}
/// A storage slot key that tracks whether it holds a plain (unhashed) EVM slot
/// or a keccak256-hashed slot.
///
/// This enum replaces the `use_hashed_state: bool` parameter pattern by carrying
/// provenance with the key itself. Once tagged at a read/write boundary, downstream
/// code can call [`Self::to_hashed`] without risk of double-hashing — hashing a
/// [`StorageSlotKey::Hashed`] is a no-op.
///
/// The on-disk encoding is unchanged (raw 32-byte [`B256`]). The variant is set
/// by the code that knows the context (which table, which storage mode).
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum StorageSlotKey {
/// An unhashed EVM storage slot, as produced by REVM execution.
Plain(B256),
/// A keccak256-hashed storage slot, as stored in `HashedStorages` and
/// in v2-mode `StorageChangeSets`.
Hashed(B256),
}
impl Default for StorageSlotKey {
fn default() -> Self {
Self::Plain(B256::ZERO)
}
}
impl StorageSlotKey {
/// Create a plain slot key from a REVM [`U256`] storage index.
pub const fn from_u256(slot: U256) -> Self {
Self::Plain(B256::new(slot.to_be_bytes()))
}
/// Create a plain slot key from a raw [`B256`].
pub const fn plain(key: B256) -> Self {
Self::Plain(key)
}
/// Create a hashed slot key from a raw [`B256`].
pub const fn hashed(key: B256) -> Self {
Self::Hashed(key)
}
/// Tag a raw [`B256`] based on the storage mode.
///
/// When `use_hashed_state` is true the key is assumed already hashed.
/// When false it is assumed to be a plain slot.
pub const fn from_raw(key: B256, use_hashed_state: bool) -> Self {
if use_hashed_state {
Self::Hashed(key)
} else {
Self::Plain(key)
}
}
/// Returns the raw [`B256`] regardless of variant.
pub const fn as_b256(&self) -> B256 {
match *self {
Self::Plain(b) | Self::Hashed(b) => b,
}
}
/// Returns `true` if this key is already hashed.
pub const fn is_hashed(&self) -> bool {
matches!(self, Self::Hashed(_))
}
/// Returns `true` if this key is plain (unhashed).
pub const fn is_plain(&self) -> bool {
matches!(self, Self::Plain(_))
}
/// Produce the keccak256-hashed form of this slot key.
///
/// - If already [`Hashed`](Self::Hashed), returns the inner value as-is (no double-hash).
/// - If [`Plain`](Self::Plain), applies keccak256 and returns the result.
pub fn to_hashed(&self) -> B256 {
match *self {
Self::Hashed(b) => b,
Self::Plain(b) => keccak256(b),
}
}
/// Convert a plain slot to its changeset representation.
///
/// In v2 mode (`use_hashed_state = true`), the changeset stores hashed keys,
/// so the plain key is hashed. In v1 mode, the plain key is stored as-is.
///
/// Panics (debug) if called on an already-hashed key.
pub fn to_changeset_key(self, use_hashed_state: bool) -> B256 {
debug_assert!(self.is_plain(), "to_changeset_key called on already-hashed key");
if use_hashed_state {
self.to_hashed()
} else {
self.as_b256()
}
}
/// Like [`to_changeset_key`](Self::to_changeset_key) but returns a tagged
/// [`StorageSlotKey`] instead of a raw [`B256`].
///
/// Panics (debug) if called on an already-hashed key.
pub fn to_changeset(self, use_hashed_state: bool) -> Self {
Self::from_raw(self.to_changeset_key(use_hashed_state), use_hashed_state)
}
}
impl From<StorageSlotKey> for B256 {
fn from(key: StorageSlotKey) -> Self {
key.as_b256()
}
}
/// Account storage entry.
///
/// `key` is the subkey when used as a value in the `StorageChangeSets` table.
@@ -142,14 +31,6 @@ impl StorageEntry {
pub const fn new(key: B256, value: U256) -> Self {
Self { key, value }
}
/// Tag this entry's key as a [`StorageSlotKey`] based on the storage mode.
///
/// When `use_hashed_state` is true, the key is tagged as already-hashed.
/// When false, it is tagged as plain.
pub const fn slot_key(&self, use_hashed_state: bool) -> StorageSlotKey {
StorageSlotKey::from_raw(self.key, use_hashed_state)
}
}
impl ValueWithSubKey for StorageEntry {

View File

@@ -135,7 +135,7 @@ impl StorageHistory {
let (block_address, entry) = result?;
let block_number = block_address.block_number();
let address = block_address.address();
highest_deleted_storages.insert((address, entry.key.as_b256()), block_number);
highest_deleted_storages.insert((address, entry.key), block_number);
last_changeset_pruned_block = Some(block_number);
pruned_changesets += 1;
limiter.increment_deleted_entries_count();
@@ -273,7 +273,7 @@ impl StorageHistory {
let (block_address, entry) = result?;
let block_number = block_address.block_number();
let address = block_address.address();
highest_deleted_storages.insert((address, entry.key.as_b256()), block_number);
highest_deleted_storages.insert((address, entry.key), block_number);
last_changeset_pruned_block = Some(block_number);
changesets_processed += 1;
limiter.increment_deleted_entries_count();

View File

@@ -148,7 +148,7 @@ impl StateProofProvider for StateProviderTest {
impl HashedPostStateProvider for StateProviderTest {
fn hashed_post_state(&self, bundle_state: &revm::database::BundleState) -> HashedPostState {
HashedPostState::from_bundle_state_adaptive::<KeccakKeyHasher>(bundle_state.state())
HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
}
}
@@ -160,14 +160,6 @@ impl StateProvider for StateProviderTest {
) -> ProviderResult<Option<alloy_primitives::StorageValue>> {
Ok(self.accounts.get(&account).and_then(|(storage, _)| storage.get(&storage_key).copied()))
}
fn storage_by_hashed_key(
&self,
_address: Address,
_hashed_storage_key: StorageKey,
) -> ProviderResult<Option<alloy_primitives::StorageValue>> {
Ok(None)
}
}
impl BytecodeReader for StateProviderTest {

View File

@@ -70,8 +70,8 @@ impl ExecutionWitnessRecord {
}
}
}
// BTreeMap keys are ordered, so the first key is the smallest
self.lowest_block_number = statedb.block_hashes.keys().next().copied()
// Get the lowest block number from the cache
self.lowest_block_number = statedb.block_hashes.lowest().map(|(n, _)| n)
}
/// Creates the record from the state after execution.

View File

@@ -172,7 +172,7 @@ pub trait EstimateCall: Call {
};
let gas_refund = match res.result {
ExecutionResult::Success { gas_refunded, .. } => gas_refunded,
ExecutionResult::Success { gas, .. } => gas.inner_refunded(),
ExecutionResult::Halt { reason, .. } => {
// here we don't check for invalid opcode because already executed with highest gas
// limit

View File

@@ -154,14 +154,6 @@ impl StateProvider for StateProviderTraitObjWrapper {
self.0.storage(account, storage_key)
}
fn storage_by_hashed_key(
&self,
address: Address,
hashed_storage_key: alloy_primitives::StorageKey,
) -> reth_errors::ProviderResult<Option<alloy_primitives::StorageValue>> {
self.0.storage_by_hashed_key(address, hashed_storage_key)
}
fn account_code(
&self,
addr: &Address,

View File

@@ -125,9 +125,7 @@ pub trait FromEvmError<Evm: ConfigureEvm>:
match result {
ExecutionResult::Success { output, .. } => Ok(output.into_data()),
ExecutionResult::Revert { output, .. } => Err(Self::from_revert(output)),
ExecutionResult::Halt { reason, gas_used } => {
Err(Self::from_evm_halt(reason, gas_used))
}
ExecutionResult::Halt { reason, gas } => Err(Self::from_evm_halt(reason, gas.used())),
}
}
}

View File

@@ -352,7 +352,7 @@ where
let mut log_index = 0;
for (index, (result, tx)) in results.into_iter().zip(block.body().transactions()).enumerate() {
let call = match result {
ExecutionResult::Halt { reason, gas_used } => {
ExecutionResult::Halt { reason, gas } => {
let error = Err::from_evm_halt(reason, tx.gas_limit());
#[allow(clippy::needless_update)]
SimCallResult {
@@ -362,12 +362,12 @@ where
code: SIMULATE_VM_ERROR_CODE,
..SimulateError::invalid_params()
}),
gas_used,
gas_used: gas.used(),
logs: Vec::new(),
status: false,
}
}
ExecutionResult::Revert { output, gas_used } => {
ExecutionResult::Revert { output, gas } => {
let error = Err::from_revert(output.clone());
#[allow(clippy::needless_update)]
SimCallResult {
@@ -377,15 +377,15 @@ where
code: SIMULATE_REVERT_CODE,
..SimulateError::invalid_params()
}),
gas_used,
gas_used: gas.used(),
status: false,
logs: Vec::new(),
}
}
ExecutionResult::Success { output, gas_used, logs, .. } => SimCallResult {
ExecutionResult::Success { output, gas, logs, .. } => SimCallResult {
return_data: output.into_data(),
error: None,
gas_used,
gas_used: gas.used(),
logs: logs
.into_iter()
.map(|log| {

View File

@@ -22,7 +22,6 @@ use reth_stages_api::{
UnwindInput, UnwindOutput,
};
use reth_static_file_types::StaticFileSegment;
use reth_trie::KeccakKeyHasher;
use std::{
cmp::{max, Ordering},
collections::BTreeMap,
@@ -462,16 +461,9 @@ where
}
}
// Write output. When `use_hashed_state` is enabled, `write_state` skips writing to
// plain account/storage tables and only writes bytecodes and changesets. The hashed
// state is then written separately below.
// write output
provider.write_state(&state, OriginalValuesKnown::Yes, StateWriteConfig::default())?;
if provider.cached_storage_settings().use_hashed_state {
let hashed_state = state.hash_state_slow::<KeccakKeyHasher>();
provider.write_hashed_state(&hashed_state.into_sorted())?;
}
let db_write_duration = time.elapsed();
debug!(
target: "sync::stages::execution",

View File

@@ -9,9 +9,7 @@ use reth_db_api::{
};
use reth_etl::Collector;
use reth_primitives_traits::Account;
use reth_provider::{
AccountExtReader, DBProvider, HashingWriter, StatsReader, StorageSettingsCache,
};
use reth_provider::{AccountExtReader, DBProvider, HashingWriter, StatsReader};
use reth_stages_api::{
AccountHashingCheckpoint, EntitiesCheckpoint, ExecInput, ExecOutput, Stage, StageCheckpoint,
StageError, StageId, UnwindInput, UnwindOutput,
@@ -136,11 +134,7 @@ impl Default for AccountHashingStage {
impl<Provider> Stage<Provider> for AccountHashingStage
where
Provider: DBProvider<Tx: DbTxMut>
+ HashingWriter
+ AccountExtReader
+ StatsReader
+ StorageSettingsCache,
Provider: DBProvider<Tx: DbTxMut> + HashingWriter + AccountExtReader + StatsReader,
{
/// Return the id of the stage
fn id(&self) -> StageId {
@@ -148,21 +142,11 @@ where
}
/// Execute the stage.
///
/// When `use_hashed_state` is enabled, this stage is a no-op because the execution stage
/// writes directly to `HashedAccounts`. Otherwise, it hashes plain state to populate hashed
/// tables.
fn execute(&mut self, provider: &Provider, input: ExecInput) -> Result<ExecOutput, StageError> {
if input.target_reached() {
return Ok(ExecOutput::done(input.checkpoint()))
}
// If using hashed state as canonical, execution already writes to `HashedAccounts`,
// so this stage becomes a no-op.
if provider.cached_storage_settings().use_hashed_state {
return Ok(ExecOutput::done(input.checkpoint().with_block_number(input.target())));
}
let (from_block, to_block) = input.next_block_range().into_inner();
// if there are more blocks then threshold it is faster to go over Plain state and hash all
@@ -250,14 +234,10 @@ where
provider: &Provider,
input: UnwindInput,
) -> Result<UnwindOutput, StageError> {
// NOTE: this runs in both v1 and v2 mode. In v2 mode, execution writes
// directly to `HashedAccounts`, but the unwind must still revert those
// entries here because `MerkleUnwind` runs after this stage (in unwind
// order) and needs `HashedAccounts` to reflect the target block state
// before it can verify the state root.
let (range, unwind_progress, _) =
input.unwind_block_range_with_threshold(self.commit_threshold);
// Aggregate all transition changesets and make a list of accounts that have been changed.
provider.unwind_account_hashing_range(range)?;
let mut stage_checkpoint =

View File

@@ -15,7 +15,6 @@ use reth_stages_api::{
EntitiesCheckpoint, ExecInput, ExecOutput, Stage, StageCheckpoint, StageError, StageId,
StorageHashingCheckpoint, UnwindInput, UnwindOutput,
};
use reth_storage_api::StorageSettingsCache;
use reth_storage_errors::provider::ProviderResult;
use std::{
fmt::Debug,
@@ -69,11 +68,7 @@ impl Default for StorageHashingStage {
impl<Provider> Stage<Provider> for StorageHashingStage
where
Provider: DBProvider<Tx: DbTxMut>
+ StorageReader
+ HashingWriter
+ StatsReader
+ StorageSettingsCache,
Provider: DBProvider<Tx: DbTxMut> + StorageReader + HashingWriter + StatsReader,
{
/// Return the id of the stage
fn id(&self) -> StageId {
@@ -87,12 +82,6 @@ where
return Ok(ExecOutput::done(input.checkpoint()))
}
// If use_hashed_state is enabled, execution writes directly to `HashedStorages`,
// so this stage becomes a no-op.
if provider.cached_storage_settings().use_hashed_state {
return Ok(ExecOutput::done(input.checkpoint().with_block_number(input.target())));
}
let (from_block, to_block) = input.next_block_range().into_inner();
// if there are more blocks then threshold it is faster to go over Plain state and hash all
@@ -187,11 +176,6 @@ where
provider: &Provider,
input: UnwindInput,
) -> Result<UnwindOutput, StageError> {
// NOTE: this runs in both v1 and v2 mode. In v2 mode, execution writes
// directly to `HashedStorages`, but the unwind must still revert those
// entries here because `MerkleUnwind` runs after this stage (in unwind
// order) and needs `HashedStorages` to reflect the target block state
// before it can verify the state root.
let (range, unwind_progress, _) =
input.unwind_block_range_with_threshold(self.commit_threshold);

View File

@@ -9,7 +9,7 @@ use reth_db_api::{
use reth_primitives_traits::{GotExpected, SealedHeader};
use reth_provider::{
ChangeSetReader, DBProvider, HeaderProvider, ProviderError, StageCheckpointReader,
StageCheckpointWriter, StatsReader, StorageChangeSetReader, StorageSettingsCache, TrieWriter,
StageCheckpointWriter, StatsReader, StorageChangeSetReader, TrieWriter,
};
use reth_stages_api::{
BlockErrorKind, EntitiesCheckpoint, ExecInput, ExecOutput, MerkleCheckpoint, Stage,
@@ -160,7 +160,6 @@ where
+ HeaderProvider
+ ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ StageCheckpointReader
+ StageCheckpointWriter,
{

View File

@@ -208,10 +208,7 @@ where
for (idx, changeset_result) in walker.enumerate() {
let (BlockNumberAddress((block_number, address)), storage) = changeset_result?;
cache
.entry(AddressStorageKey((address, storage.key.as_b256())))
.or_default()
.push(block_number);
cache.entry(AddressStorageKey((address, storage.key))).or_default().push(block_number);
if idx > 0 && idx % interval == 0 && total_changesets > 1000 {
info!(target: "sync::stages::index_history", progress = %format!("{:.4}%", (idx as f64 / total_changesets as f64) * 100.0), "Collecting indices");

View File

@@ -3,7 +3,7 @@
use alloy_consensus::{constants::ETH_TO_WEI, Header, TxEip1559, TxReceipt};
use alloy_eips::eip1559::INITIAL_BASE_FEE;
use alloy_genesis::{Genesis, GenesisAccount};
use alloy_primitives::{bytes, keccak256, Address, Bytes, TxKind, B256, U256};
use alloy_primitives::{bytes, Address, Bytes, TxKind, B256, U256};
use reth_chainspec::{ChainSpecBuilder, ChainSpecProvider, MAINNET};
use reth_config::config::StageConfig;
use reth_consensus::noop::NoopConsensus;
@@ -36,7 +36,7 @@ use reth_stages::sets::DefaultStages;
use reth_stages_api::{Pipeline, StageId};
use reth_static_file::StaticFileProducer;
use reth_storage_api::{
ChangeSetReader, StateProvider, StorageChangeSetReader, StorageSettings, StorageSettingsCache,
ChangeSetReader, StateProvider, StorageChangeSetReader, StorageSettingsCache,
};
use reth_testing_utils::generators::{self, generate_key, sign_tx_with_key_pair};
use reth_trie::{HashedPostState, KeccakKeyHasher, StateRoot};
@@ -89,11 +89,6 @@ fn assert_changesets_queryable(
"storage changesets should be queryable from static files for blocks {:?}",
block_range
);
// Verify keys are in hashed format (v2 mode)
for (_, entry) in &storage_changesets {
assert!(entry.key.is_hashed(), "v2: storage changeset keys should be tagged as hashed");
}
} else {
let storage_changesets: Vec<_> = provider
.tx_ref()
@@ -105,16 +100,6 @@ fn assert_changesets_queryable(
"storage changesets should be queryable from MDBX for blocks {:?}",
block_range
);
// Verify keys are plain (not hashed) in v1 mode
for (_, entry) in &storage_changesets {
let key = entry.key;
assert_ne!(
key,
keccak256(key),
"v1: storage changeset key should be plain (not its own keccak256)"
);
}
}
// Verify account changesets
@@ -216,22 +201,19 @@ where
pipeline
}
/// Shared helper for pipeline forward sync and unwind tests.
/// Tests pipeline with ALL stages enabled using both ETH transfers and contract storage changes.
///
/// This test:
/// 1. Pre-funds a signer account and deploys a Counter contract in genesis
/// 2. Each block contains two transactions:
/// - ETH transfer to a recipient (account state changes)
/// - Counter `increment()` call (storage state changes)
/// 3. Runs the full pipeline with ALL stages enabled
/// 4. Forward syncs to `num_blocks`, unwinds to `unwind_target`, then re-syncs back to `num_blocks`
/// 4. Forward syncs to block 5, unwinds to block 2
///
/// When `storage_settings` is `Some`, the pipeline provider factory is configured with the given
/// settings before genesis initialization (e.g. v2 storage mode).
async fn run_pipeline_forward_and_unwind(
storage_settings: Option<StorageSettings>,
num_blocks: u64,
unwind_target: u64,
) -> eyre::Result<()> {
/// This exercises both account and storage hashing/history stages.
#[tokio::test(flavor = "multi_thread")]
async fn test_pipeline() -> eyre::Result<()> {
reth_tracing::init_test_tracing();
// Generate a keypair for signing transactions
@@ -277,6 +259,7 @@ async fn run_pipeline_forward_and_unwind(
let evm_config = EthEvmConfig::new(chain_spec.clone());
// Build blocks by actually executing transactions to get correct state roots
let num_blocks = 5u64;
let mut blocks: Vec<SealedBlock<Block>> = Vec::new();
let mut parent_hash = genesis.hash();
@@ -359,7 +342,7 @@ async fn run_pipeline_forward_and_unwind(
// Convert bundle state to hashed post state and compute state root
let hashed_state =
HashedPostState::from_bundle_state_adaptive::<KeccakKeyHasher>(output.state.state());
HashedPostState::from_bundle_state::<KeccakKeyHasher>(output.state.state());
let (state_root, _trie_updates) = StateRoot::overlay_root_with_updates(
provider.tx_ref(),
&hashed_state.clone().into_sorted(),
@@ -401,15 +384,11 @@ async fn run_pipeline_forward_and_unwind(
// This is needed because we wrote state during block generation for computing state roots
let pipeline_provider_factory =
create_test_provider_factory_with_chain_spec(chain_spec.clone());
if let Some(settings) = storage_settings {
pipeline_provider_factory.set_storage_settings_cache(settings);
}
init_genesis(&pipeline_provider_factory).expect("init genesis");
let pipeline_genesis =
pipeline_provider_factory.sealed_header(0)?.expect("genesis should exist");
let pipeline_consensus = NoopConsensus::arc();
let blocks_clone = blocks.clone();
let file_client = create_file_client_from_blocks(blocks);
let max_block = file_client.max_block().unwrap();
let tip = file_client.tip().expect("tip");
@@ -438,7 +417,7 @@ async fn run_pipeline_forward_and_unwind(
{
let provider = pipeline_provider_factory.provider()?;
let last_block = provider.last_block_number()?;
assert_eq!(last_block, num_blocks, "should have synced {num_blocks} blocks");
assert_eq!(last_block, 5, "should have synced 5 blocks");
for stage_id in [
StageId::Headers,
@@ -456,28 +435,29 @@ async fn run_pipeline_forward_and_unwind(
let checkpoint = provider.get_stage_checkpoint(stage_id)?;
assert_eq!(
checkpoint.map(|c| c.block_number),
Some(num_blocks),
"{stage_id} checkpoint should be at block {num_blocks}"
Some(5),
"{stage_id} checkpoint should be at block 5"
);
}
// Verify the counter contract's storage was updated
// After num_blocks blocks with 1 increment each, slot 0 should be num_blocks
// After 5 blocks with 1 increment each, slot 0 should be 5
let state = provider.latest();
let counter_storage = state.storage(CONTRACT_ADDRESS, B256::ZERO)?;
assert_eq!(
counter_storage,
Some(U256::from(num_blocks)),
"Counter storage slot 0 should be {num_blocks} after {num_blocks} increments"
Some(U256::from(5)),
"Counter storage slot 0 should be 5 after 5 increments"
);
}
// Verify changesets are queryable before unwind
// This validates that the #21561 fix works - unwind needs to read changesets from the correct
// source
assert_changesets_queryable(&pipeline_provider_factory, 1..=num_blocks)?;
assert_changesets_queryable(&pipeline_provider_factory, 1..=5)?;
// Unwind to unwind_target
// Unwind to block 2
let unwind_target = 2u64;
pipeline.unwind(unwind_target, None)?;
// Verify unwind
@@ -504,114 +484,7 @@ async fn run_pipeline_forward_and_unwind(
);
}
}
let state = provider.latest();
let counter_storage = state.storage(CONTRACT_ADDRESS, B256::ZERO)?;
assert_eq!(
counter_storage,
Some(U256::from(unwind_target)),
"Counter storage slot 0 should be {unwind_target} after unwinding to block {unwind_target}"
);
}
// Re-sync: build a new pipeline starting from unwind_target and sync back to num_blocks
let resync_file_client = create_file_client_from_blocks(blocks_clone);
let resync_consensus = NoopConsensus::arc();
let resync_stages_config = StageConfig::default();
let unwind_head = pipeline_provider_factory
.sealed_header(unwind_target)?
.expect("unwind target header should exist");
let mut resync_header_downloader =
ReverseHeadersDownloaderBuilder::new(resync_stages_config.headers)
.build(resync_file_client.clone(), resync_consensus.clone())
.into_task();
resync_header_downloader.update_local_head(unwind_head);
resync_header_downloader.update_sync_target(SyncTarget::Tip(tip));
let mut resync_body_downloader = BodiesDownloaderBuilder::new(resync_stages_config.bodies)
.build(resync_file_client, resync_consensus, pipeline_provider_factory.clone())
.into_task();
resync_body_downloader
.set_download_range(unwind_target + 1..=max_block)
.expect("set download range");
let resync_pipeline = build_pipeline(
pipeline_provider_factory.clone(),
resync_header_downloader,
resync_body_downloader,
max_block,
tip,
);
let (_resync_pipeline, resync_result) = resync_pipeline.run_as_fut(None).await;
resync_result?;
// Verify re-sync
{
let provider = pipeline_provider_factory.provider()?;
let last_block = provider.last_block_number()?;
assert_eq!(last_block, num_blocks, "should have re-synced to {num_blocks} blocks");
for stage_id in [
StageId::Headers,
StageId::Bodies,
StageId::SenderRecovery,
StageId::Execution,
StageId::AccountHashing,
StageId::StorageHashing,
StageId::MerkleExecute,
StageId::TransactionLookup,
StageId::IndexAccountHistory,
StageId::IndexStorageHistory,
StageId::Finish,
] {
let checkpoint = provider.get_stage_checkpoint(stage_id)?;
assert_eq!(
checkpoint.map(|c| c.block_number),
Some(num_blocks),
"{stage_id} checkpoint should be at block {num_blocks} after re-sync"
);
}
let state = provider.latest();
let counter_storage = state.storage(CONTRACT_ADDRESS, B256::ZERO)?;
assert_eq!(
counter_storage,
Some(U256::from(num_blocks)),
"Counter storage slot 0 should be {num_blocks} after re-sync"
);
}
Ok(())
}
/// Tests pipeline with ALL stages enabled using both ETH transfers and contract storage changes.
///
/// This test:
/// 1. Pre-funds a signer account and deploys a Counter contract in genesis
/// 2. Each block contains two transactions:
/// - ETH transfer to a recipient (account state changes)
/// - Counter `increment()` call (storage state changes)
/// 3. Runs the full pipeline with ALL stages enabled
/// 4. Forward syncs to block 5, unwinds to block 2, then re-syncs to block 5
///
/// This exercises both account and storage hashing/history stages.
#[tokio::test(flavor = "multi_thread")]
async fn test_pipeline() -> eyre::Result<()> {
run_pipeline_forward_and_unwind(None, 5, 2).await
}
/// Same as [`test_pipeline`] but runs with v2 storage settings (`use_hashed_state=true`,
/// `storage_changesets_in_static_files=true`, etc.).
///
/// In v2 mode:
/// - The execution stage writes directly to `HashedAccounts`/`HashedStorages`
/// - `AccountHashingStage` and `StorageHashingStage` are no-ops during forward execution
/// - Changesets are stored in static files with pre-hashed storage keys
/// - Unwind must still revert hashed state via the hashing stages before `MerkleUnwind` validates
#[tokio::test(flavor = "multi_thread")]
async fn test_pipeline_v2() -> eyre::Result<()> {
run_pipeline_forward_and_unwind(Some(StorageSettings::v2()), 5, 2).await
}

View File

@@ -43,19 +43,11 @@ pub struct StorageSettings {
impl StorageSettings {
/// Returns the default base `StorageSettings`.
///
/// When the `edge` feature is enabled, returns [`Self::v2()`] so that CI and
/// edge builds automatically use v2 storage defaults. Otherwise returns
/// [`Self::v1()`]. The `--storage.v2` CLI flag can also opt into v2 at runtime
/// regardless of feature flags.
/// Always returns [`Self::v1()`]. Use the `--storage.v2` CLI flag to opt into
/// [`Self::v2()`] at runtime. The `rocksdb` feature only makes the v2 backend
/// *available*; it does not activate it by default.
pub const fn base() -> Self {
#[cfg(feature = "edge")]
{
Self::v2()
}
#[cfg(not(feature = "edge"))]
{
Self::v1()
}
Self::v1()
}
/// Creates `StorageSettings` for v2 nodes with all storage features enabled:
@@ -73,7 +65,7 @@ impl StorageSettings {
storages_history_in_rocksdb: true,
transaction_hash_numbers_in_rocksdb: true,
account_history_in_rocksdb: true,
use_hashed_state: true,
use_hashed_state: false,
}
}

View File

@@ -5,7 +5,8 @@ use crate::ProviderResult;
use alloy_primitives::BlockNumber;
use reth_db::models::AccountBeforeTx;
use reth_db_api::models::BlockNumberAddress;
use reth_storage_api::{ChangeSetReader, ChangesetEntry, StorageChangeSetReader};
use reth_primitives_traits::StorageEntry;
use reth_storage_api::{ChangeSetReader, StorageChangeSetReader};
use std::ops::{Bound, RangeBounds};
/// Iterator that walks account changesets from static files in a block range.
@@ -109,7 +110,7 @@ pub struct StaticFileStorageChangesetWalker<P> {
/// Current block being processed
current_block: BlockNumber,
/// Changesets for current block
current_changesets: Vec<(BlockNumberAddress, ChangesetEntry)>,
current_changesets: Vec<(BlockNumberAddress, StorageEntry)>,
/// Index within current block's changesets
changeset_index: usize,
}
@@ -143,7 +144,7 @@ impl<P> Iterator for StaticFileStorageChangesetWalker<P>
where
P: StorageChangeSetReader,
{
type Item = ProviderResult<(BlockNumberAddress, ChangesetEntry)>;
type Item = ProviderResult<(BlockNumberAddress, StorageEntry)>;
fn next(&mut self) -> Option<Self::Item> {
if let Some(changeset) = self.current_changesets.get(self.changeset_index).copied() {

View File

@@ -23,13 +23,11 @@ use reth_chainspec::ChainInfo;
use reth_db_api::models::{AccountBeforeTx, BlockNumberAddress, StoredBlockBodyIndices};
use reth_execution_types::ExecutionOutcome;
use reth_node_types::{BlockTy, HeaderTy, NodeTypesWithDB, ReceiptTy, TxTy};
use reth_primitives_traits::{Account, RecoveredBlock, SealedHeader};
use reth_primitives_traits::{Account, RecoveredBlock, SealedHeader, StorageEntry};
use reth_prune_types::{PruneCheckpoint, PruneSegment};
use reth_stages_types::{StageCheckpoint, StageId};
use reth_static_file_types::StaticFileSegment;
use reth_storage_api::{
BlockBodyIndicesProvider, ChangesetEntry, NodePrimitivesProvider, StorageChangeSetReader,
};
use reth_storage_api::{BlockBodyIndicesProvider, NodePrimitivesProvider, StorageChangeSetReader};
use reth_storage_errors::provider::ProviderResult;
use reth_trie::{HashedPostState, KeccakKeyHasher};
use revm_database::BundleState;
@@ -621,7 +619,7 @@ impl<N: ProviderNodeTypes> StateProviderFactory for BlockchainProvider<N> {
impl<N: NodeTypesWithDB> HashedPostStateProvider for BlockchainProvider<N> {
fn hashed_post_state(&self, bundle_state: &BundleState) -> HashedPostState {
HashedPostState::from_bundle_state_adaptive::<KeccakKeyHasher>(bundle_state.state())
HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
}
}
@@ -715,7 +713,7 @@ impl<N: ProviderNodeTypes> StorageChangeSetReader for BlockchainProvider<N> {
fn storage_changeset(
&self,
block_number: BlockNumber,
) -> ProviderResult<Vec<(BlockNumberAddress, ChangesetEntry)>> {
) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
self.consistent_provider()?.storage_changeset(block_number)
}
@@ -724,14 +722,14 @@ impl<N: ProviderNodeTypes> StorageChangeSetReader for BlockchainProvider<N> {
block_number: BlockNumber,
address: Address,
storage_key: B256,
) -> ProviderResult<Option<ChangesetEntry>> {
) -> ProviderResult<Option<StorageEntry>> {
self.consistent_provider()?.get_storage_before_block(block_number, address, storage_key)
}
fn storage_changesets_range(
&self,
range: impl RangeBounds<BlockNumber>,
) -> ProviderResult<Vec<(BlockNumberAddress, ChangesetEntry)>> {
) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
self.consistent_provider()?.storage_changesets_range(range)
}

View File

@@ -21,16 +21,13 @@ use reth_chainspec::ChainInfo;
use reth_db_api::models::{AccountBeforeTx, BlockNumberAddress, StoredBlockBodyIndices};
use reth_execution_types::{BundleStateInit, ExecutionOutcome, RevertsInit};
use reth_node_types::{BlockTy, HeaderTy, ReceiptTy, TxTy};
use reth_primitives_traits::{
Account, BlockBody, RecoveredBlock, SealedHeader, StorageEntry, StorageSlotKey,
};
use reth_primitives_traits::{Account, BlockBody, RecoveredBlock, SealedHeader, StorageEntry};
use reth_prune_types::{PruneCheckpoint, PruneSegment};
use reth_stages_types::{StageCheckpoint, StageId};
use reth_static_file_types::StaticFileSegment;
use reth_storage_api::{
BlockBodyIndicesProvider, ChangesetEntry, DatabaseProviderFactory, NodePrimitivesProvider,
StateProvider, StateProviderBox, StorageChangeSetReader, StorageSettingsCache,
TryIntoHistoricalStateProvider,
BlockBodyIndicesProvider, DatabaseProviderFactory, NodePrimitivesProvider, StateProvider,
StateProviderBox, StorageChangeSetReader, TryIntoHistoricalStateProvider,
};
use reth_storage_errors::provider::ProviderResult;
use revm_database::states::PlainStorageRevert;
@@ -217,16 +214,13 @@ impl<N: ProviderNodeTypes> ConsistentProvider<N> {
)))
}
/// Populate a [`BundleStateInit`] and [`RevertsInit`] based on the given storage and account
/// changesets.
///
/// When `use_hashed_state` is enabled, storage changeset keys are already hashed, so current
/// values are read directly from [`reth_db_api::tables::HashedStorages`]. Otherwise, values
/// are read via [`StateProvider::storage`] which queries plain state tables.
/// Populate a [`BundleStateInit`] and [`RevertsInit`] using cursors over the
/// [`reth_db::PlainAccountState`] and [`reth_db::PlainStorageState`] tables, based on the given
/// storage and account changesets.
fn populate_bundle_state(
&self,
account_changeset: Vec<(u64, AccountBeforeTx)>,
storage_changeset: Vec<(BlockNumberAddress, ChangesetEntry)>,
storage_changeset: Vec<(BlockNumberAddress, StorageEntry)>,
block_range_end: BlockNumber,
) -> ProviderResult<(BundleStateInit, RevertsInit)> {
let mut state: BundleStateInit = HashMap::default();
@@ -263,16 +257,10 @@ impl<N: ProviderNodeTypes> ConsistentProvider<N> {
};
// match storage.
match account_state.2.entry(old_storage.key.as_b256()) {
match account_state.2.entry(old_storage.key) {
hash_map::Entry::Vacant(entry) => {
let new_storage_value = match old_storage.key {
StorageSlotKey::Hashed(_) => state_provider
.storage_by_hashed_key(address, old_storage.key.as_b256())?
.unwrap_or_default(),
StorageSlotKey::Plain(_) => state_provider
.storage(address, old_storage.key.as_b256())?
.unwrap_or_default(),
};
let new_storage_value =
state_provider.storage(address, old_storage.key)?.unwrap_or_default();
entry.insert((old_storage.value, new_storage_value));
}
hash_map::Entry::Occupied(mut entry) => {
@@ -286,7 +274,7 @@ impl<N: ProviderNodeTypes> ConsistentProvider<N> {
.entry(address)
.or_default()
.1
.push(StorageEntry::from(old_storage));
.push(old_storage);
}
Ok((state, reverts))
@@ -1312,8 +1300,7 @@ impl<N: ProviderNodeTypes> StorageChangeSetReader for ConsistentProvider<N> {
fn storage_changeset(
&self,
block_number: BlockNumber,
) -> ProviderResult<Vec<(BlockNumberAddress, ChangesetEntry)>> {
let use_hashed = self.storage_provider.cached_storage_settings().use_hashed_state;
) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
if let Some(state) =
self.head_block.as_ref().and_then(|b| b.block_on_chain(block_number.into()))
{
@@ -1329,10 +1316,9 @@ impl<N: ProviderNodeTypes> StorageChangeSetReader for ConsistentProvider<N> {
.flatten()
.flat_map(|revert: PlainStorageRevert| {
revert.storage_revert.into_iter().map(move |(key, value)| {
let tagged_key = StorageSlotKey::from_u256(key).to_changeset(use_hashed);
(
BlockNumberAddress((block_number, revert.address)),
ChangesetEntry { key: tagged_key, value: value.to_previous_value() },
StorageEntry { key: key.into(), value: value.to_previous_value() },
)
})
})
@@ -1367,8 +1353,7 @@ impl<N: ProviderNodeTypes> StorageChangeSetReader for ConsistentProvider<N> {
block_number: BlockNumber,
address: Address,
storage_key: B256,
) -> ProviderResult<Option<ChangesetEntry>> {
let use_hashed = self.storage_provider.cached_storage_settings().use_hashed_state;
) -> ProviderResult<Option<StorageEntry>> {
if let Some(state) =
self.head_block.as_ref().and_then(|b| b.block_on_chain(block_number.into()))
{
@@ -1387,11 +1372,9 @@ impl<N: ProviderNodeTypes> StorageChangeSetReader for ConsistentProvider<N> {
return None
}
revert.storage_revert.into_iter().find_map(|(key, value)| {
let tagged_key = StorageSlotKey::from_u256(key).to_changeset(use_hashed);
(tagged_key.as_b256() == storage_key).then(|| ChangesetEntry {
key: tagged_key,
value: value.to_previous_value(),
})
let key = key.into();
(key == storage_key)
.then(|| StorageEntry { key, value: value.to_previous_value() })
})
});
Ok(changeset)
@@ -1415,14 +1398,12 @@ impl<N: ProviderNodeTypes> StorageChangeSetReader for ConsistentProvider<N> {
fn storage_changesets_range(
&self,
range: impl RangeBounds<BlockNumber>,
) -> ProviderResult<Vec<(BlockNumberAddress, ChangesetEntry)>> {
) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
let range = to_range(range);
let mut changesets = Vec::new();
let database_start = range.start;
let mut database_end = range.end;
let use_hashed = self.storage_provider.cached_storage_settings().use_hashed_state;
if let Some(head_block) = &self.head_block {
database_end = head_block.anchor().number;
@@ -1440,14 +1421,9 @@ impl<N: ProviderNodeTypes> StorageChangeSetReader for ConsistentProvider<N> {
.flatten()
.flat_map(|revert: PlainStorageRevert| {
revert.storage_revert.into_iter().map(move |(key, value)| {
let tagged_key =
StorageSlotKey::from_u256(key).to_changeset(use_hashed);
(
BlockNumberAddress((state.number(), revert.address)),
ChangesetEntry {
key: tagged_key,
value: value.to_previous_value(),
},
StorageEntry { key: key.into(), value: value.to_previous_value() },
)
})
});
@@ -2084,648 +2060,4 @@ mod tests {
Ok(())
}
#[test]
fn test_get_state_storage_value_hashed_state() -> eyre::Result<()> {
use alloy_primitives::{keccak256, U256};
use reth_db_api::{models::StorageSettings, tables, transaction::DbTxMut};
use reth_primitives_traits::StorageEntry;
use reth_storage_api::StorageSettingsCache;
use std::collections::HashMap;
let address = alloy_primitives::Address::with_last_byte(1);
let account = reth_primitives_traits::Account {
nonce: 1,
balance: U256::from(1000),
bytecode_hash: None,
};
let slot = U256::from(0x42);
let slot_b256 = B256::from(slot);
let hashed_address = keccak256(address);
let hashed_slot = keccak256(slot_b256);
let mut rng = generators::rng();
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v2());
let blocks = random_block_range(
&mut rng,
0..=1,
BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..1, ..Default::default() },
);
let provider_rw = factory.provider_rw()?;
provider_rw.append_blocks_with_state(
blocks
.into_iter()
.map(|b| b.try_recover().expect("failed to seal block with senders"))
.collect(),
&ExecutionOutcome {
bundle: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::ZERO, U256::from(100)));
s
})],
[
Vec::new(),
vec![(address, Some(Some(account.into())), vec![(slot, U256::ZERO)])],
],
[],
),
first_block: 0,
..Default::default()
},
Default::default(),
)?;
provider_rw.tx_ref().put::<tables::HashedStorages>(
hashed_address,
StorageEntry { key: hashed_slot, value: U256::from(100) },
)?;
provider_rw.tx_ref().put::<tables::HashedAccounts>(hashed_address, account)?;
provider_rw.commit()?;
let provider = BlockchainProvider::new(factory)?;
let consistent_provider = provider.consistent_provider()?;
let outcome =
consistent_provider.get_state(1..=1)?.expect("should return execution outcome");
let state = &outcome.bundle.state;
let account_state = state.get(&address).expect("should have account in bundle state");
let storage = &account_state.storage;
let slot_as_u256 = U256::from_be_bytes(*hashed_slot);
let storage_slot = storage.get(&slot_as_u256).expect("should have the slot in storage");
assert_eq!(
storage_slot.present_value,
U256::from(100),
"present_value should be 100 (the actual value in HashedStorages)"
);
Ok(())
}
#[test]
#[cfg(all(unix, feature = "rocksdb"))]
fn test_get_state_storage_value_hashed_state_historical() -> eyre::Result<()> {
use alloy_primitives::{keccak256, U256};
use reth_db_api::{models::StorageSettings, tables, transaction::DbTxMut};
use reth_primitives_traits::StorageEntry;
use reth_storage_api::StorageSettingsCache;
use std::collections::HashMap;
let address = alloy_primitives::Address::with_last_byte(1);
let account = reth_primitives_traits::Account {
nonce: 1,
balance: U256::from(1000),
bytecode_hash: None,
};
let slot = U256::from(0x42);
let slot_b256 = B256::from(slot);
let hashed_address = keccak256(address);
let hashed_slot = keccak256(slot_b256);
let mut rng = generators::rng();
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v2());
let blocks = random_block_range(
&mut rng,
0..=3,
BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..1, ..Default::default() },
);
let provider_rw = factory.provider_rw()?;
provider_rw.append_blocks_with_state(
blocks
.into_iter()
.map(|b| b.try_recover().expect("failed to seal block with senders"))
.collect(),
&ExecutionOutcome {
bundle: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::ZERO, U256::from(300)));
s
})],
[
Vec::new(),
vec![(address, Some(Some(account.into())), vec![(slot, U256::ZERO)])],
vec![(address, Some(Some(account.into())), vec![(slot, U256::from(100))])],
vec![(address, Some(Some(account.into())), vec![(slot, U256::from(200))])],
],
[],
),
first_block: 0,
..Default::default()
},
Default::default(),
)?;
provider_rw.tx_ref().put::<tables::HashedStorages>(
hashed_address,
StorageEntry { key: hashed_slot, value: U256::from(300) },
)?;
provider_rw.tx_ref().put::<tables::HashedAccounts>(hashed_address, account)?;
provider_rw.commit()?;
let provider = BlockchainProvider::new(factory)?;
let consistent_provider = provider.consistent_provider()?;
let outcome =
consistent_provider.get_state(1..=2)?.expect("should return execution outcome");
let state = &outcome.bundle.state;
let account_state = state.get(&address).expect("should have account in bundle state");
let storage = &account_state.storage;
let slot_as_u256 = U256::from_be_bytes(*hashed_slot);
let storage_slot = storage.get(&slot_as_u256).expect("should have the slot in storage");
assert_eq!(
storage_slot.present_value,
U256::from(200),
"present_value should be 200 (the value at block 2, not 300 which is the latest)"
);
Ok(())
}
#[test]
fn test_get_state_storage_value_plain_state() -> eyre::Result<()> {
use alloy_primitives::U256;
use reth_db_api::{models::StorageSettings, tables, transaction::DbTxMut};
use reth_primitives_traits::StorageEntry;
use reth_storage_api::StorageSettingsCache;
use std::collections::HashMap;
let address = alloy_primitives::Address::with_last_byte(1);
let account = reth_primitives_traits::Account {
nonce: 1,
balance: U256::from(1000),
bytecode_hash: None,
};
let slot = U256::from(0x42);
let slot_b256 = B256::from(slot);
let mut rng = generators::rng();
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v1());
let blocks = random_block_range(
&mut rng,
0..=1,
BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..1, ..Default::default() },
);
let provider_rw = factory.provider_rw()?;
provider_rw.append_blocks_with_state(
blocks
.into_iter()
.map(|b| b.try_recover().expect("failed to seal block with senders"))
.collect(),
&ExecutionOutcome {
bundle: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::ZERO, U256::from(100)));
s
})],
[
Vec::new(),
vec![(address, Some(Some(account.into())), vec![(slot, U256::ZERO)])],
],
[],
),
first_block: 0,
..Default::default()
},
Default::default(),
)?;
provider_rw.tx_ref().put::<tables::PlainStorageState>(
address,
StorageEntry { key: slot_b256, value: U256::from(100) },
)?;
provider_rw.tx_ref().put::<tables::PlainAccountState>(address, account)?;
provider_rw.commit()?;
let provider = BlockchainProvider::new(factory)?;
let consistent_provider = provider.consistent_provider()?;
let outcome =
consistent_provider.get_state(1..=1)?.expect("should return execution outcome");
let state = &outcome.bundle.state;
let account_state = state.get(&address).expect("should have account in bundle state");
let storage = &account_state.storage;
let storage_slot = storage.get(&slot).expect("should have the slot in storage");
assert_eq!(
storage_slot.present_value,
U256::from(100),
"present_value should be 100 (the actual value in PlainStorageState)"
);
Ok(())
}
#[test]
fn test_storage_changeset_consistent_keys_hashed_state() -> eyre::Result<()> {
use alloy_primitives::{keccak256, U256};
use reth_db_api::models::StorageSettings;
use reth_storage_api::{StorageChangeSetReader, StorageSettingsCache};
use std::collections::HashMap;
let mut rng = generators::rng();
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v2());
let (database_blocks, in_memory_blocks) = random_blocks(&mut rng, 1, 1, None, None, 0..1);
let address = alloy_primitives::Address::with_last_byte(1);
let account = reth_primitives_traits::Account {
nonce: 1,
balance: U256::from(1000),
bytecode_hash: None,
};
let slot = U256::from(0x42);
let provider_rw = factory.provider_rw()?;
provider_rw.append_blocks_with_state(
database_blocks
.into_iter()
.map(|b| b.try_recover().expect("failed to seal block with senders"))
.collect(),
&ExecutionOutcome {
bundle: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::ZERO, U256::from(100)));
s
})],
[[(address, Some(Some(account.into())), vec![(slot, U256::ZERO)])]],
[],
),
first_block: 0,
..Default::default()
},
Default::default(),
)?;
provider_rw.commit()?;
let provider = BlockchainProvider::new(factory)?;
let in_mem_block = in_memory_blocks.first().unwrap();
let senders = in_mem_block.senders().expect("failed to recover senders");
let chain = NewCanonicalChain::Commit {
new: vec![ExecutedBlock {
recovered_block: Arc::new(RecoveredBlock::new_sealed(
in_mem_block.clone(),
senders,
)),
execution_output: Arc::new(BlockExecutionOutput {
state: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::from(100), U256::from(200)));
s
})],
[[(address, Some(Some(account.into())), vec![(slot, U256::from(100))])]],
[],
),
result: BlockExecutionResult {
receipts: Default::default(),
requests: Default::default(),
gas_used: 0,
blob_gas_used: 0,
},
}),
..Default::default()
}],
};
provider.canonical_in_memory_state.update_chain(chain);
let consistent_provider = provider.consistent_provider()?;
let db_changeset = consistent_provider.storage_changeset(0)?;
let mem_changeset = consistent_provider.storage_changeset(1)?;
let slot_b256 = B256::from(slot);
let _hashed_slot_b256 = keccak256(slot_b256);
assert_eq!(db_changeset.len(), 1);
assert_eq!(mem_changeset.len(), 1);
let db_key = db_changeset[0].1.key;
let mem_key = mem_changeset[0].1.key;
assert_eq!(
db_key, mem_key,
"DB and in-memory changesets should return the same key format (hashed) for the same logical slot"
);
Ok(())
}
#[test]
fn test_storage_changeset_consistent_keys_plain_state() -> eyre::Result<()> {
use alloy_primitives::U256;
use reth_db_api::models::StorageSettings;
use reth_storage_api::{StorageChangeSetReader, StorageSettingsCache};
use std::collections::HashMap;
let mut rng = generators::rng();
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v1());
let (database_blocks, in_memory_blocks) = random_blocks(&mut rng, 1, 1, None, None, 0..1);
let address = alloy_primitives::Address::with_last_byte(1);
let account = reth_primitives_traits::Account {
nonce: 1,
balance: U256::from(1000),
bytecode_hash: None,
};
let slot = U256::from(0x42);
let provider_rw = factory.provider_rw()?;
provider_rw.append_blocks_with_state(
database_blocks
.into_iter()
.map(|b| b.try_recover().expect("failed to seal block with senders"))
.collect(),
&ExecutionOutcome {
bundle: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::ZERO, U256::from(100)));
s
})],
[[(address, Some(Some(account.into())), vec![(slot, U256::ZERO)])]],
[],
),
first_block: 0,
..Default::default()
},
Default::default(),
)?;
provider_rw.commit()?;
let provider = BlockchainProvider::new(factory)?;
let in_mem_block = in_memory_blocks.first().unwrap();
let senders = in_mem_block.senders().expect("failed to recover senders");
let chain = NewCanonicalChain::Commit {
new: vec![ExecutedBlock {
recovered_block: Arc::new(RecoveredBlock::new_sealed(
in_mem_block.clone(),
senders,
)),
execution_output: Arc::new(BlockExecutionOutput {
state: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::from(100), U256::from(200)));
s
})],
[[(address, Some(Some(account.into())), vec![(slot, U256::from(100))])]],
[],
),
result: BlockExecutionResult {
receipts: Default::default(),
requests: Default::default(),
gas_used: 0,
blob_gas_used: 0,
},
}),
..Default::default()
}],
};
provider.canonical_in_memory_state.update_chain(chain);
let consistent_provider = provider.consistent_provider()?;
let db_changeset = consistent_provider.storage_changeset(0)?;
let mem_changeset = consistent_provider.storage_changeset(1)?;
let slot_b256 = B256::from(slot);
assert_eq!(db_changeset.len(), 1);
assert_eq!(mem_changeset.len(), 1);
let db_key = db_changeset[0].1.key.as_b256();
let mem_key = mem_changeset[0].1.key.as_b256();
assert_eq!(db_key, slot_b256, "DB changeset should use plain (unhashed) key");
assert_eq!(mem_key, slot_b256, "In-memory changeset should use plain (unhashed) key");
assert_eq!(
db_key, mem_key,
"DB and in-memory changesets should return the same key format (plain) for the same logical slot"
);
Ok(())
}
#[test]
fn test_storage_changesets_range_consistent_keys_hashed_state() -> eyre::Result<()> {
use alloy_primitives::U256;
use reth_db_api::models::StorageSettings;
use reth_storage_api::{StorageChangeSetReader, StorageSettingsCache};
use std::collections::HashMap;
let mut rng = generators::rng();
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v2());
let (database_blocks, in_memory_blocks) = random_blocks(&mut rng, 2, 1, None, None, 0..1);
let address = alloy_primitives::Address::with_last_byte(1);
let account = reth_primitives_traits::Account {
nonce: 1,
balance: U256::from(1000),
bytecode_hash: None,
};
let slot = U256::from(0x42);
let provider_rw = factory.provider_rw()?;
provider_rw.append_blocks_with_state(
database_blocks
.into_iter()
.map(|b| b.try_recover().expect("failed to seal block with senders"))
.collect(),
&ExecutionOutcome {
bundle: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::ZERO, U256::from(100)));
s
})],
vec![
vec![(address, Some(Some(account.into())), vec![(slot, U256::ZERO)])],
vec![],
],
[],
),
first_block: 0,
..Default::default()
},
Default::default(),
)?;
provider_rw.commit()?;
let provider = BlockchainProvider::new(factory)?;
let in_mem_block = in_memory_blocks.first().unwrap();
let senders = in_mem_block.senders().expect("failed to recover senders");
let chain = NewCanonicalChain::Commit {
new: vec![ExecutedBlock {
recovered_block: Arc::new(RecoveredBlock::new_sealed(
in_mem_block.clone(),
senders,
)),
execution_output: Arc::new(BlockExecutionOutput {
state: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::from(100), U256::from(200)));
s
})],
[[(address, Some(Some(account.into())), vec![(slot, U256::from(100))])]],
[],
),
result: BlockExecutionResult {
receipts: Default::default(),
requests: Default::default(),
gas_used: 0,
blob_gas_used: 0,
},
}),
..Default::default()
}],
};
provider.canonical_in_memory_state.update_chain(chain);
let consistent_provider = provider.consistent_provider()?;
let all_changesets = consistent_provider.storage_changesets_range(0..=2)?;
assert_eq!(all_changesets.len(), 2, "should have one changeset entry per block");
let keys: Vec<B256> = all_changesets.iter().map(|(_, entry)| entry.key.as_b256()).collect();
assert_eq!(
keys[0], keys[1],
"same logical slot should produce identical keys whether from DB or memory"
);
Ok(())
}
#[test]
fn test_storage_changesets_range_consistent_keys_plain_state() -> eyre::Result<()> {
use alloy_primitives::U256;
use reth_db_api::models::StorageSettings;
use reth_storage_api::{StorageChangeSetReader, StorageSettingsCache};
use std::collections::HashMap;
let mut rng = generators::rng();
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v1());
let (database_blocks, in_memory_blocks) = random_blocks(&mut rng, 2, 1, None, None, 0..1);
let address = alloy_primitives::Address::with_last_byte(1);
let account = reth_primitives_traits::Account {
nonce: 1,
balance: U256::from(1000),
bytecode_hash: None,
};
let slot = U256::from(0x42);
let provider_rw = factory.provider_rw()?;
provider_rw.append_blocks_with_state(
database_blocks
.into_iter()
.map(|b| b.try_recover().expect("failed to seal block with senders"))
.collect(),
&ExecutionOutcome {
bundle: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::ZERO, U256::from(100)));
s
})],
vec![
vec![(address, Some(Some(account.into())), vec![(slot, U256::ZERO)])],
vec![],
],
[],
),
first_block: 0,
..Default::default()
},
Default::default(),
)?;
provider_rw.commit()?;
let provider = BlockchainProvider::new(factory)?;
let in_mem_block = in_memory_blocks.first().unwrap();
let senders = in_mem_block.senders().expect("failed to recover senders");
let chain = NewCanonicalChain::Commit {
new: vec![ExecutedBlock {
recovered_block: Arc::new(RecoveredBlock::new_sealed(
in_mem_block.clone(),
senders,
)),
execution_output: Arc::new(BlockExecutionOutput {
state: BundleState::new(
[(address, None, Some(account.into()), {
let mut s = HashMap::default();
s.insert(slot, (U256::from(100), U256::from(200)));
s
})],
[[(address, Some(Some(account.into())), vec![(slot, U256::from(100))])]],
[],
),
result: BlockExecutionResult {
receipts: Default::default(),
requests: Default::default(),
gas_used: 0,
blob_gas_used: 0,
},
}),
..Default::default()
}],
};
provider.canonical_in_memory_state.update_chain(chain);
let consistent_provider = provider.consistent_provider()?;
let all_changesets = consistent_provider.storage_changesets_range(0..=2)?;
assert_eq!(all_changesets.len(), 2, "should have one changeset entry per block");
let slot_b256 = B256::from(slot);
let keys: Vec<B256> = all_changesets.iter().map(|(_, entry)| entry.key.as_b256()).collect();
assert_eq!(
keys[0], keys[1],
"same logical slot should produce identical keys whether from DB or memory"
);
assert_eq!(
keys[0], slot_b256,
"keys should be plain/unhashed when use_hashed_state is false"
);
Ok(())
}
}

View File

@@ -719,7 +719,7 @@ impl<N: ProviderNodeTypes> PruneCheckpointReader for ProviderFactory<N> {
impl<N: ProviderNodeTypes> HashedPostStateProvider for ProviderFactory<N> {
fn hashed_post_state(&self, bundle_state: &BundleState) -> HashedPostState {
HashedPostState::from_bundle_state_adaptive::<KeccakKeyHasher>(bundle_state.state())
HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -317,10 +317,7 @@ impl RocksDBProvider {
let unique_keys: HashSet<_> = changesets
.into_iter()
.map(|(block_addr, entry)| {
// entry.key is a hashed storage key
(block_addr.address(), entry.key.as_b256(), checkpoint + 1)
})
.map(|(block_addr, entry)| (block_addr.address(), entry.key, checkpoint + 1))
.collect();
let indices: Vec<_> = unique_keys.into_iter().collect();

View File

@@ -2,7 +2,6 @@ use super::metrics::{RocksDBMetrics, RocksDBOperation, ROCKSDB_TABLES};
use crate::providers::{compute_history_rank, needs_prev_shard_check, HistoryInfo};
use alloy_consensus::transaction::TxHashRef;
use alloy_primitives::{
keccak256,
map::{AddressMap, HashMap},
Address, BlockNumber, TxNumber, B256,
};
@@ -1337,8 +1336,7 @@ impl RocksDBProvider {
for storage_block_reverts in reverts.storage {
for revert in storage_block_reverts {
for (slot, _) in revert.storage_revert {
let plain_key = B256::new(slot.to_be_bytes());
let key = keccak256(plain_key);
let key = B256::new(slot.to_be_bytes());
storage_history
.entry((revert.address, key))
.or_default()

View File

@@ -11,7 +11,7 @@ use reth_db_api::{
transaction::DbTx,
BlockNumberList,
};
use reth_primitives_traits::{Account, Bytecode, StorageSlotKey};
use reth_primitives_traits::{Account, Bytecode};
use reth_storage_api::{
BlockNumReader, BytecodeReader, DBProvider, NodePrimitivesProvider, StateProofProvider,
StorageChangeSetReader, StorageRootProvider, StorageSettingsCache,
@@ -26,8 +26,8 @@ use reth_trie::{
TrieInputSorted,
};
use reth_trie_db::{
hashed_storage_from_reverts_with_provider, DatabaseProof, DatabaseStateRoot,
DatabaseStorageProof, DatabaseStorageRoot, DatabaseTrieWitness,
hashed_storage_from_reverts_with_provider, DatabaseHashedPostState, DatabaseProof,
DatabaseStateRoot, DatabaseStorageProof, DatabaseStorageRoot, DatabaseTrieWitness,
};
use std::fmt::Debug;
@@ -150,7 +150,7 @@ impl<'b, Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + Block
pub fn storage_history_lookup(
&self,
address: Address,
storage_key: StorageSlotKey,
storage_key: StorageKey,
) -> ProviderResult<HistoryInfo>
where
Provider: StorageSettingsCache + RocksDBProviderFactory + NodePrimitivesProvider,
@@ -159,85 +159,17 @@ impl<'b, Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + Block
return Err(ProviderError::StateAtBlockPruned(self.block_number))
}
let lookup_key = if self.provider.cached_storage_settings().use_hashed_state {
storage_key.to_hashed()
} else {
debug_assert!(
storage_key.is_plain(),
"expected plain storage key when use_hashed_state is false"
);
storage_key.as_b256()
};
self.provider.with_rocksdb_tx(|rocks_tx_ref| {
let mut reader = EitherReader::new_storages_history(self.provider, rocks_tx_ref)?;
reader.storage_history_info(
address,
lookup_key,
storage_key,
self.block_number,
self.lowest_available_blocks.storage_history_block_number,
)
})
}
/// Resolves a storage value by looking up the given key in history, changesets, or
/// plain state.
///
/// Accepts a [`StorageSlotKey`]; the correct lookup key is derived internally
/// based on the storage mode.
fn storage_by_lookup_key(
&self,
address: Address,
storage_key: StorageSlotKey,
) -> ProviderResult<Option<StorageValue>>
where
Provider: StorageSettingsCache + RocksDBProviderFactory + NodePrimitivesProvider,
{
let lookup_key = if self.provider.cached_storage_settings().use_hashed_state {
storage_key.to_hashed()
} else {
debug_assert!(
storage_key.is_plain(),
"expected plain storage key when use_hashed_state is false"
);
storage_key.as_b256()
};
match self.storage_history_lookup(address, storage_key)? {
HistoryInfo::NotYetWritten => Ok(None),
HistoryInfo::InChangeset(changeset_block_number) => self
.provider
.get_storage_before_block(changeset_block_number, address, lookup_key)?
.ok_or_else(|| ProviderError::StorageChangesetNotFound {
block_number: changeset_block_number,
address,
storage_key: Box::new(lookup_key),
})
.map(|entry| entry.value)
.map(Some),
HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => {
if self.provider.cached_storage_settings().use_hashed_state {
let hashed_address = alloy_primitives::keccak256(address);
Ok(self
.tx()
.cursor_dup_read::<tables::HashedStorages>()?
.seek_by_key_subkey(hashed_address, lookup_key)?
.filter(|entry| entry.key == lookup_key)
.map(|entry| entry.value)
.or(Some(StorageValue::ZERO)))
} else {
Ok(self
.tx()
.cursor_dup_read::<tables::PlainStorageState>()?
.seek_by_key_subkey(address, lookup_key)?
.filter(|entry| entry.key == lookup_key)
.map(|entry| entry.value)
.or(Some(StorageValue::ZERO)))
}
}
}
}
/// Checks and returns `true` if distance to historical block exceeds the provided limit.
fn check_distance_against_limit(&self, limit: u64) -> ProviderResult<bool> {
let tip = self.provider.last_block_number()?;
@@ -246,10 +178,7 @@ impl<'b, Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + Block
}
/// Retrieve revert hashed state for this history provider.
fn revert_state(&self) -> ProviderResult<HashedPostStateSorted>
where
Provider: StorageSettingsCache,
{
fn revert_state(&self) -> ProviderResult<HashedPostStateSorted> {
if !self.lowest_available_blocks.is_account_history_available(self.block_number) ||
!self.lowest_available_blocks.is_storage_history_available(self.block_number)
{
@@ -264,14 +193,11 @@ impl<'b, Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + Block
);
}
reth_trie_db::from_reverts_auto(self.provider, self.block_number..)
HashedPostStateSorted::from_reverts::<KeccakKeyHasher>(self.provider, self.block_number..)
}
/// Retrieve revert hashed storage for this history provider and target address.
fn revert_storage(&self, address: Address) -> ProviderResult<HashedStorage>
where
Provider: StorageSettingsCache,
{
fn revert_storage(&self, address: Address) -> ProviderResult<HashedStorage> {
if !self.lowest_available_blocks.is_storage_history_available(self.block_number) {
return Err(ProviderError::StateAtBlockPruned(self.block_number))
}
@@ -337,12 +263,7 @@ impl<
.map(|account_before| account_before.info)
}
HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => {
if self.provider.cached_storage_settings().use_hashed_state {
let hashed_address = alloy_primitives::keccak256(address);
Ok(self.tx().get_by_encoded_key::<tables::HashedAccounts>(&hashed_address)?)
} else {
Ok(self.tx().get_by_encoded_key::<tables::PlainAccountState>(address)?)
}
Ok(self.tx().get_by_encoded_key::<tables::PlainAccountState>(address)?)
}
}
}
@@ -365,13 +286,8 @@ impl<Provider: DBProvider + BlockNumReader + BlockHashReader> BlockHashReader
}
}
impl<
Provider: DBProvider
+ ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ StorageSettingsCache,
> StateRootProvider for HistoricalStateProviderRef<'_, Provider>
impl<Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + BlockNumReader>
StateRootProvider for HistoricalStateProviderRef<'_, Provider>
{
fn state_root(&self, hashed_state: HashedPostState) -> ProviderResult<B256> {
let mut revert_state = self.revert_state()?;
@@ -407,13 +323,8 @@ impl<
}
}
impl<
Provider: DBProvider
+ ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ StorageSettingsCache,
> StorageRootProvider for HistoricalStateProviderRef<'_, Provider>
impl<Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + BlockNumReader>
StorageRootProvider for HistoricalStateProviderRef<'_, Provider>
{
fn storage_root(
&self,
@@ -451,13 +362,8 @@ impl<
}
}
impl<
Provider: DBProvider
+ ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ StorageSettingsCache,
> StateProofProvider for HistoricalStateProviderRef<'_, Provider>
impl<Provider: DBProvider + ChangeSetReader + StorageChangeSetReader + BlockNumReader>
StateProofProvider for HistoricalStateProviderRef<'_, Provider>
{
/// Get account and storage proofs.
fn proof(
@@ -491,7 +397,7 @@ impl<
impl<Provider> HashedPostStateProvider for HistoricalStateProviderRef<'_, Provider> {
fn hashed_post_state(&self, bundle_state: &revm_database::BundleState) -> HashedPostState {
HashedPostState::from_bundle_state_adaptive::<KeccakKeyHasher>(bundle_state.state())
HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
}
}
@@ -506,24 +412,32 @@ impl<
+ NodePrimitivesProvider,
> StateProvider for HistoricalStateProviderRef<'_, Provider>
{
/// Expects a plain (unhashed) storage key slot.
/// Get storage.
fn storage(
&self,
address: Address,
storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
self.storage_by_lookup_key(address, StorageSlotKey::plain(storage_key))
}
fn storage_by_hashed_key(
&self,
address: Address,
hashed_storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
if !self.provider.cached_storage_settings().use_hashed_state {
return Err(ProviderError::UnsupportedProvider)
match self.storage_history_lookup(address, storage_key)? {
HistoryInfo::NotYetWritten => Ok(None),
HistoryInfo::InChangeset(changeset_block_number) => self
.provider
.get_storage_before_block(changeset_block_number, address, storage_key)?
.ok_or_else(|| ProviderError::StorageChangesetNotFound {
block_number: changeset_block_number,
address,
storage_key: Box::new(storage_key),
})
.map(|entry| entry.value)
.map(Some),
HistoryInfo::InPlainState | HistoryInfo::MaybeInPlainState => Ok(self
.tx()
.cursor_dup_read::<tables::PlainStorageState>()?
.seek_by_key_subkey(address, storage_key)?
.filter(|entry| entry.key == storage_key)
.map(|entry| entry.value)
.or(Some(StorageValue::ZERO))),
}
self.storage_by_lookup_key(address, StorageSlotKey::hashed(hashed_storage_key))
}
}
@@ -716,7 +630,7 @@ mod tests {
transaction::{DbTx, DbTxMut},
BlockNumberList,
};
use reth_primitives_traits::{Account, StorageEntry, StorageSlotKey};
use reth_primitives_traits::{Account, StorageEntry};
use reth_storage_api::{
BlockHashReader, BlockNumReader, ChangeSetReader, DBProvider, DatabaseProviderFactory,
NodePrimitivesProvider, StorageChangeSetReader, StorageSettingsCache,
@@ -971,7 +885,7 @@ mod tests {
Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
));
assert!(matches!(
provider.storage_history_lookup(ADDRESS, StorageSlotKey::plain(STORAGE)),
provider.storage_history_lookup(ADDRESS, STORAGE),
Err(ProviderError::StateAtBlockPruned(number)) if number == provider.block_number
));
@@ -990,7 +904,7 @@ mod tests {
Ok(HistoryInfo::MaybeInPlainState)
));
assert!(matches!(
provider.storage_history_lookup(ADDRESS, StorageSlotKey::plain(STORAGE)),
provider.storage_history_lookup(ADDRESS, STORAGE),
Ok(HistoryInfo::MaybeInPlainState)
));
@@ -1009,7 +923,7 @@ mod tests {
Ok(HistoryInfo::MaybeInPlainState)
));
assert!(matches!(
provider.storage_history_lookup(ADDRESS, StorageSlotKey::plain(STORAGE)),
provider.storage_history_lookup(ADDRESS, STORAGE),
Ok(HistoryInfo::MaybeInPlainState)
));
}
@@ -1029,242 +943,6 @@ mod tests {
assert_eq!(HistoryInfo::from_lookup(None, false, None), HistoryInfo::InPlainState);
}
#[test]
fn history_provider_get_storage_legacy() {
let factory = create_test_provider_factory();
assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state);
let tx = factory.provider_rw().unwrap().into_tx();
tx.put::<tables::StoragesHistory>(
StorageShardedKey {
address: ADDRESS,
sharded_key: ShardedKey { key: STORAGE, highest_block_number: 7 },
},
BlockNumberList::new([3, 7]).unwrap(),
)
.unwrap();
tx.put::<tables::StoragesHistory>(
StorageShardedKey {
address: ADDRESS,
sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
},
BlockNumberList::new([10, 15]).unwrap(),
)
.unwrap();
tx.put::<tables::StoragesHistory>(
StorageShardedKey {
address: HIGHER_ADDRESS,
sharded_key: ShardedKey { key: STORAGE, highest_block_number: u64::MAX },
},
BlockNumberList::new([4]).unwrap(),
)
.unwrap();
let higher_entry_plain = StorageEntry { key: STORAGE, value: U256::from(1000) };
let higher_entry_at4 = StorageEntry { key: STORAGE, value: U256::from(0) };
let entry_plain = StorageEntry { key: STORAGE, value: U256::from(100) };
let entry_at15 = StorageEntry { key: STORAGE, value: U256::from(15) };
let entry_at10 = StorageEntry { key: STORAGE, value: U256::from(10) };
let entry_at7 = StorageEntry { key: STORAGE, value: U256::from(7) };
let entry_at3 = StorageEntry { key: STORAGE, value: U256::from(0) };
tx.put::<tables::StorageChangeSets>((3, ADDRESS).into(), entry_at3).unwrap();
tx.put::<tables::StorageChangeSets>((4, HIGHER_ADDRESS).into(), higher_entry_at4).unwrap();
tx.put::<tables::StorageChangeSets>((7, ADDRESS).into(), entry_at7).unwrap();
tx.put::<tables::StorageChangeSets>((10, ADDRESS).into(), entry_at10).unwrap();
tx.put::<tables::StorageChangeSets>((15, ADDRESS).into(), entry_at15).unwrap();
tx.put::<tables::PlainStorageState>(ADDRESS, entry_plain).unwrap();
tx.put::<tables::PlainStorageState>(HIGHER_ADDRESS, higher_entry_plain).unwrap();
tx.commit().unwrap();
let db = factory.provider().unwrap();
assert!(matches!(
HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
Ok(None)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
Ok(Some(U256::ZERO))
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
Ok(Some(expected_value)) if expected_value == entry_at7.value
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
Ok(Some(expected_value)) if expected_value == entry_at7.value
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
Ok(Some(expected_value)) if expected_value == entry_at10.value
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
Ok(Some(expected_value)) if expected_value == entry_at10.value
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
Ok(Some(expected_value)) if expected_value == entry_at15.value
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
Ok(Some(expected_value)) if expected_value == entry_plain.value
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
Ok(None)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
Ok(Some(expected_value)) if expected_value == higher_entry_plain.value
));
}
#[test]
#[cfg(all(unix, feature = "rocksdb"))]
fn history_provider_get_storage_hashed_state() {
use crate::BlockWriter;
use alloy_primitives::keccak256;
use reth_db_api::models::StorageSettings;
use reth_execution_types::ExecutionOutcome;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use revm_database::BundleState;
use std::collections::HashMap;
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v2());
let slot = U256::from_be_bytes(*STORAGE);
let account: revm_state::AccountInfo =
Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None }.into();
let higher_account: revm_state::AccountInfo =
Account { nonce: 1, balance: U256::from(2000), bytecode_hash: None }.into();
let mut rng = generators::rng();
let blocks = random_block_range(
&mut rng,
0..=15,
BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..1, ..Default::default() },
);
let mut addr_storage = HashMap::default();
addr_storage.insert(slot, (U256::ZERO, U256::from(100)));
let mut higher_storage = HashMap::default();
higher_storage.insert(slot, (U256::ZERO, U256::from(1000)));
type Revert = Vec<(Address, Option<Option<revm_state::AccountInfo>>, Vec<(U256, U256)>)>;
let mut reverts: Vec<Revert> = vec![Vec::new(); 16];
reverts[3] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::ZERO)])];
reverts[4] =
vec![(HIGHER_ADDRESS, Some(Some(higher_account.clone())), vec![(slot, U256::ZERO)])];
reverts[7] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(7))])];
reverts[10] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(10))])];
reverts[15] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(15))])];
let bundle = BundleState::new(
[
(ADDRESS, None, Some(account), addr_storage),
(HIGHER_ADDRESS, None, Some(higher_account), higher_storage),
],
reverts,
[],
);
let provider_rw = factory.provider_rw().unwrap();
provider_rw
.append_blocks_with_state(
blocks
.into_iter()
.map(|b| b.try_recover().expect("failed to seal block with senders"))
.collect(),
&ExecutionOutcome { bundle, first_block: 0, ..Default::default() },
Default::default(),
)
.unwrap();
let hashed_address = keccak256(ADDRESS);
let hashed_higher_address = keccak256(HIGHER_ADDRESS);
let hashed_storage = keccak256(STORAGE);
provider_rw
.tx_ref()
.put::<tables::HashedStorages>(
hashed_address,
StorageEntry { key: hashed_storage, value: U256::from(100) },
)
.unwrap();
provider_rw
.tx_ref()
.put::<tables::HashedStorages>(
hashed_higher_address,
StorageEntry { key: hashed_storage, value: U256::from(1000) },
)
.unwrap();
provider_rw
.tx_ref()
.put::<tables::HashedAccounts>(
hashed_address,
Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None },
)
.unwrap();
provider_rw
.tx_ref()
.put::<tables::HashedAccounts>(
hashed_higher_address,
Account { nonce: 1, balance: U256::from(2000), bytecode_hash: None },
)
.unwrap();
provider_rw.commit().unwrap();
let db = factory.provider().unwrap();
assert!(matches!(
HistoricalStateProviderRef::new(&db, 0).storage(ADDRESS, STORAGE),
Ok(None)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 3).storage(ADDRESS, STORAGE),
Ok(Some(U256::ZERO))
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 4).storage(ADDRESS, STORAGE),
Ok(Some(v)) if v == U256::from(7)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 7).storage(ADDRESS, STORAGE),
Ok(Some(v)) if v == U256::from(7)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 9).storage(ADDRESS, STORAGE),
Ok(Some(v)) if v == U256::from(10)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 10).storage(ADDRESS, STORAGE),
Ok(Some(v)) if v == U256::from(10)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 11).storage(ADDRESS, STORAGE),
Ok(Some(v)) if v == U256::from(15)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 16).storage(ADDRESS, STORAGE),
Ok(Some(v)) if v == U256::from(100)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 1).storage(HIGHER_ADDRESS, STORAGE),
Ok(None)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 1000).storage(HIGHER_ADDRESS, STORAGE),
Ok(Some(v)) if v == U256::from(1000)
));
}
#[test]
fn test_needs_prev_shard_check() {
// Only needs check when rank == 0 and found_block != block_number
@@ -1273,105 +951,4 @@ mod tests {
assert!(!needs_prev_shard_check(0, Some(5), 5)); // found_block == block_number
assert!(!needs_prev_shard_check(1, Some(10), 5)); // rank > 0
}
#[test]
fn test_historical_storage_by_hashed_key_unsupported_in_v1() {
let factory = create_test_provider_factory();
assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state);
let db = factory.provider().unwrap();
let provider = HistoricalStateProviderRef::new(&db, 1);
assert!(matches!(
provider.storage_by_hashed_key(ADDRESS, STORAGE),
Err(ProviderError::UnsupportedProvider)
));
}
#[test]
#[cfg(all(unix, feature = "rocksdb"))]
fn test_historical_storage_by_hashed_key_v2() {
use crate::BlockWriter;
use alloy_primitives::keccak256;
use reth_db_api::models::StorageSettings;
use reth_execution_types::ExecutionOutcome;
use reth_testing_utils::generators::{self, random_block_range, BlockRangeParams};
use revm_database::BundleState;
use std::collections::HashMap;
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v2());
let slot = U256::from_be_bytes(*STORAGE);
let hashed_storage = keccak256(STORAGE);
let account: revm_state::AccountInfo =
Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None }.into();
let mut rng = generators::rng();
let blocks = random_block_range(
&mut rng,
0..=5,
BlockRangeParams { parent: Some(B256::ZERO), tx_count: 0..1, ..Default::default() },
);
let mut addr_storage = HashMap::default();
addr_storage.insert(slot, (U256::ZERO, U256::from(100)));
type Revert = Vec<(Address, Option<Option<revm_state::AccountInfo>>, Vec<(U256, U256)>)>;
let mut reverts: Vec<Revert> = vec![Vec::new(); 6];
reverts[3] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::ZERO)])];
reverts[5] = vec![(ADDRESS, Some(Some(account.clone())), vec![(slot, U256::from(50))])];
let bundle = BundleState::new([(ADDRESS, None, Some(account), addr_storage)], reverts, []);
let provider_rw = factory.provider_rw().unwrap();
provider_rw
.append_blocks_with_state(
blocks
.into_iter()
.map(|b| b.try_recover().expect("failed to seal block with senders"))
.collect(),
&ExecutionOutcome { bundle, first_block: 0, ..Default::default() },
Default::default(),
)
.unwrap();
let hashed_address = keccak256(ADDRESS);
provider_rw
.tx_ref()
.put::<tables::HashedStorages>(
hashed_address,
StorageEntry { key: hashed_storage, value: U256::from(100) },
)
.unwrap();
provider_rw
.tx_ref()
.put::<tables::HashedAccounts>(
hashed_address,
Account { nonce: 1, balance: U256::from(1000), bytecode_hash: None },
)
.unwrap();
provider_rw.commit().unwrap();
let db = factory.provider().unwrap();
assert!(matches!(
HistoricalStateProviderRef::new(&db, 0).storage_by_hashed_key(ADDRESS, hashed_storage),
Ok(None)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 3).storage_by_hashed_key(ADDRESS, hashed_storage),
Ok(Some(U256::ZERO))
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 4).storage_by_hashed_key(ADDRESS, hashed_storage),
Ok(Some(v)) if v == U256::from(50)
));
assert!(matches!(
HistoricalStateProviderRef::new(&db, 4).storage_by_hashed_key(ADDRESS, STORAGE),
Ok(None | Some(U256::ZERO))
));
}
}

View File

@@ -4,9 +4,7 @@ use crate::{
use alloy_primitives::{Address, BlockNumber, Bytes, StorageKey, StorageValue, B256};
use reth_db_api::{cursor::DbDupCursorRO, tables, transaction::DbTx};
use reth_primitives_traits::{Account, Bytecode};
use reth_storage_api::{
BytecodeReader, DBProvider, StateProofProvider, StorageRootProvider, StorageSettingsCache,
};
use reth_storage_api::{BytecodeReader, DBProvider, StateProofProvider, StorageRootProvider};
use reth_storage_errors::provider::{ProviderError, ProviderResult};
use reth_trie::{
proof::{Proof, StorageProof},
@@ -35,33 +33,12 @@ impl<'b, Provider: DBProvider> LatestStateProviderRef<'b, Provider> {
fn tx(&self) -> &Provider::Tx {
self.0.tx_ref()
}
fn hashed_storage_lookup(
&self,
hashed_address: B256,
hashed_slot: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
let mut cursor = self.tx().cursor_dup_read::<tables::HashedStorages>()?;
Ok(cursor
.seek_by_key_subkey(hashed_address, hashed_slot)?
.filter(|e| e.key == hashed_slot)
.map(|e| e.value))
}
}
impl<Provider: DBProvider + StorageSettingsCache> AccountReader
for LatestStateProviderRef<'_, Provider>
{
impl<Provider: DBProvider> AccountReader for LatestStateProviderRef<'_, Provider> {
/// Get basic account information.
fn basic_account(&self, address: &Address) -> ProviderResult<Option<Account>> {
if self.0.cached_storage_settings().use_hashed_state {
let hashed_address = alloy_primitives::keccak256(address);
self.tx()
.get_by_encoded_key::<tables::HashedAccounts>(&hashed_address)
.map_err(Into::into)
} else {
self.tx().get_by_encoded_key::<tables::PlainAccountState>(address).map_err(Into::into)
}
self.tx().get_by_encoded_key::<tables::PlainAccountState>(address).map_err(Into::into)
}
}
@@ -167,45 +144,26 @@ impl<Provider: DBProvider> StateProofProvider for LatestStateProviderRef<'_, Pro
impl<Provider: DBProvider> HashedPostStateProvider for LatestStateProviderRef<'_, Provider> {
fn hashed_post_state(&self, bundle_state: &revm_database::BundleState) -> HashedPostState {
HashedPostState::from_bundle_state_adaptive::<KeccakKeyHasher>(bundle_state.state())
HashedPostState::from_bundle_state::<KeccakKeyHasher>(bundle_state.state())
}
}
impl<Provider: DBProvider + BlockHashReader + StorageSettingsCache> StateProvider
impl<Provider: DBProvider + BlockHashReader> StateProvider
for LatestStateProviderRef<'_, Provider>
{
/// Get storage by plain (unhashed) storage key slot.
/// Get storage.
fn storage(
&self,
account: Address,
storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
if self.0.cached_storage_settings().use_hashed_state {
self.hashed_storage_lookup(
alloy_primitives::keccak256(account),
alloy_primitives::keccak256(storage_key),
)
} else {
let mut cursor = self.tx().cursor_dup_read::<tables::PlainStorageState>()?;
if let Some(entry) = cursor.seek_by_key_subkey(account, storage_key)? &&
entry.key == storage_key
{
return Ok(Some(entry.value));
}
Ok(None)
}
}
fn storage_by_hashed_key(
&self,
address: Address,
hashed_storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
if self.0.cached_storage_settings().use_hashed_state {
self.hashed_storage_lookup(alloy_primitives::keccak256(address), hashed_storage_key)
} else {
Err(ProviderError::UnsupportedProvider)
let mut cursor = self.tx().cursor_dup_read::<tables::PlainStorageState>()?;
if let Some(entry) = cursor.seek_by_key_subkey(account, storage_key)? &&
entry.key == storage_key
{
return Ok(Some(entry.value))
}
Ok(None)
}
}
@@ -236,181 +194,15 @@ impl<Provider: DBProvider> LatestStateProvider<Provider> {
}
// Delegates all provider impls to [LatestStateProviderRef]
reth_storage_api::macros::delegate_provider_impls!(LatestStateProvider<Provider> where [Provider: DBProvider + BlockHashReader + StorageSettingsCache]);
reth_storage_api::macros::delegate_provider_impls!(LatestStateProvider<Provider> where [Provider: DBProvider + BlockHashReader ]);
#[cfg(test)]
mod tests {
use super::*;
use crate::test_utils::create_test_provider_factory;
use alloy_primitives::{address, b256, keccak256, U256};
use reth_db_api::{
models::StorageSettings,
tables,
transaction::{DbTx, DbTxMut},
};
use reth_primitives_traits::StorageEntry;
use reth_storage_api::StorageSettingsCache;
use reth_storage_errors::provider::ProviderError;
const fn assert_state_provider<T: StateProvider>() {}
#[expect(dead_code)]
const fn assert_latest_state_provider<
T: DBProvider + BlockHashReader + StorageSettingsCache,
>() {
const fn assert_latest_state_provider<T: DBProvider + BlockHashReader>() {
assert_state_provider::<LatestStateProvider<T>>();
}
#[test]
fn test_latest_storage_hashed_state() {
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v2());
let address = address!("0x0000000000000000000000000000000000000001");
let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
let hashed_address = keccak256(address);
let hashed_slot = keccak256(slot);
let tx = factory.provider_rw().unwrap().into_tx();
tx.put::<tables::HashedStorages>(
hashed_address,
StorageEntry { key: hashed_slot, value: U256::from(42) },
)
.unwrap();
tx.commit().unwrap();
let db = factory.provider().unwrap();
let provider_ref = LatestStateProviderRef::new(&db);
assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
let other_address = address!("0x0000000000000000000000000000000000000099");
let other_slot =
b256!("0x0000000000000000000000000000000000000000000000000000000000000099");
assert_eq!(provider_ref.storage(other_address, other_slot).unwrap(), None);
let tx = factory.provider_rw().unwrap().into_tx();
let plain_address = address!("0x0000000000000000000000000000000000000002");
let plain_slot =
b256!("0x0000000000000000000000000000000000000000000000000000000000000002");
tx.put::<tables::PlainStorageState>(
plain_address,
StorageEntry { key: plain_slot, value: U256::from(99) },
)
.unwrap();
tx.commit().unwrap();
let db = factory.provider().unwrap();
let provider_ref = LatestStateProviderRef::new(&db);
assert_eq!(provider_ref.storage(plain_address, plain_slot).unwrap(), None);
}
#[test]
fn test_latest_storage_hashed_state_returns_none_for_missing() {
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v2());
let address = address!("0x0000000000000000000000000000000000000001");
let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
let db = factory.provider().unwrap();
let provider_ref = LatestStateProviderRef::new(&db);
assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
}
#[test]
fn test_latest_storage_legacy() {
let factory = create_test_provider_factory();
assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state);
let address = address!("0x0000000000000000000000000000000000000001");
let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000005");
let tx = factory.provider_rw().unwrap().into_tx();
tx.put::<tables::PlainStorageState>(
address,
StorageEntry { key: slot, value: U256::from(42) },
)
.unwrap();
tx.commit().unwrap();
let db = factory.provider().unwrap();
let provider_ref = LatestStateProviderRef::new(&db);
assert_eq!(provider_ref.storage(address, slot).unwrap(), Some(U256::from(42)));
let other_slot =
b256!("0x0000000000000000000000000000000000000000000000000000000000000099");
assert_eq!(provider_ref.storage(address, other_slot).unwrap(), None);
}
#[test]
fn test_latest_storage_legacy_does_not_read_hashed() {
let factory = create_test_provider_factory();
assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state);
let address = address!("0x0000000000000000000000000000000000000001");
let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000005");
let hashed_address = keccak256(address);
let hashed_slot = keccak256(slot);
let tx = factory.provider_rw().unwrap().into_tx();
tx.put::<tables::HashedStorages>(
hashed_address,
StorageEntry { key: hashed_slot, value: U256::from(42) },
)
.unwrap();
tx.commit().unwrap();
let db = factory.provider().unwrap();
let provider_ref = LatestStateProviderRef::new(&db);
assert_eq!(provider_ref.storage(address, slot).unwrap(), None);
}
#[test]
fn test_latest_storage_by_hashed_key_v2() {
let factory = create_test_provider_factory();
factory.set_storage_settings_cache(StorageSettings::v2());
let address = address!("0x0000000000000000000000000000000000000001");
let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
let hashed_address = keccak256(address);
let hashed_slot = keccak256(slot);
let tx = factory.provider_rw().unwrap().into_tx();
tx.put::<tables::HashedStorages>(
hashed_address,
StorageEntry { key: hashed_slot, value: U256::from(42) },
)
.unwrap();
tx.commit().unwrap();
let db = factory.provider().unwrap();
let provider_ref = LatestStateProviderRef::new(&db);
assert_eq!(
provider_ref.storage_by_hashed_key(address, hashed_slot).unwrap(),
Some(U256::from(42))
);
assert_eq!(provider_ref.storage_by_hashed_key(address, slot).unwrap(), None);
}
#[test]
fn test_latest_storage_by_hashed_key_unsupported_in_v1() {
let factory = create_test_provider_factory();
assert!(!factory.provider().unwrap().cached_storage_settings().use_hashed_state);
let address = address!("0x0000000000000000000000000000000000000001");
let slot = b256!("0x0000000000000000000000000000000000000000000000000000000000000001");
let db = factory.provider().unwrap();
let provider_ref = LatestStateProviderRef::new(&db);
assert!(matches!(
provider_ref.storage_by_hashed_key(address, slot),
Err(ProviderError::UnsupportedProvider)
));
}
}

View File

@@ -10,15 +10,17 @@ use reth_stages_types::StageId;
use reth_storage_api::{
BlockNumReader, ChangeSetReader, DBProvider, DatabaseProviderFactory,
DatabaseProviderROFactory, PruneCheckpointReader, StageCheckpointReader,
StorageChangeSetReader, StorageSettingsCache,
StorageChangeSetReader,
};
use reth_trie::{
hashed_cursor::{HashedCursorFactory, HashedPostStateCursorFactory},
trie_cursor::{InMemoryTrieCursorFactory, TrieCursorFactory},
updates::TrieUpdatesSorted,
HashedPostStateSorted,
HashedPostStateSorted, KeccakKeyHasher,
};
use reth_trie_db::{
ChangesetCache, DatabaseHashedCursorFactory, DatabaseHashedPostState, DatabaseTrieCursorFactory,
};
use reth_trie_db::{ChangesetCache, DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
use std::{
sync::Arc,
time::{Duration, Instant},
@@ -196,8 +198,7 @@ where
+ ChangeSetReader
+ StorageChangeSetReader
+ DBProvider
+ BlockNumReader
+ StorageSettingsCache,
+ BlockNumReader,
{
/// Resolves the effective overlay (trie updates, hashed state).
///
@@ -335,7 +336,10 @@ where
let _guard = debug_span!(target: "providers::state::overlay", "Retrieving hashed state reverts").entered();
let start = Instant::now();
let res = reth_trie_db::from_reverts_auto(provider, from_block + 1..)?;
let res = HashedPostStateSorted::from_reverts::<KeccakKeyHasher>(
provider,
from_block + 1..,
)?;
retrieve_hashed_state_reverts_duration = start.elapsed();
res
};
@@ -446,8 +450,7 @@ where
+ PruneCheckpointReader
+ BlockNumReader
+ ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache,
+ StorageChangeSetReader,
{
type Provider = OverlayStateProvider<F::Provider>;

View File

@@ -34,7 +34,7 @@ use reth_nippy_jar::{NippyJar, NippyJarChecker, CONFIG_FILE_EXTENSION};
use reth_node_types::NodePrimitives;
use reth_primitives_traits::{
dashmap::DashMap, AlloyBlockHeader as _, BlockBody as _, RecoveredBlock, SealedHeader,
SignedTransaction, StorageSlotKey,
SignedTransaction, StorageEntry,
};
use reth_prune_types::PruneSegment;
use reth_stages_types::PipelineTarget;
@@ -43,7 +43,7 @@ use reth_static_file_types::{
SegmentRangeInclusive, StaticFileMap, StaticFileSegment, DEFAULT_BLOCKS_PER_STATIC_FILE,
};
use reth_storage_api::{
BlockBodyIndicesProvider, ChangeSetReader, ChangesetEntry, DBProvider, PruneCheckpointReader,
BlockBodyIndicesProvider, ChangeSetReader, DBProvider, PruneCheckpointReader,
StorageChangeSetReader, StorageSettingsCache,
};
use reth_storage_errors::provider::{ProviderError, ProviderResult, StaticFileWriterError};
@@ -643,7 +643,7 @@ impl<N: NodePrimitives> StaticFileProvider<N> {
revert.storage_revert.into_iter().map(move |(key, revert_to_slot)| {
StorageBeforeTx {
address: revert.address,
key: StorageSlotKey::from_u256(key).to_hashed(),
key: B256::new(key.to_be_bytes()),
value: revert_to_slot.to_previous_value(),
}
})
@@ -2520,7 +2520,7 @@ impl<N: NodePrimitives> StorageChangeSetReader for StaticFileProvider<N> {
fn storage_changeset(
&self,
block_number: BlockNumber,
) -> ProviderResult<Vec<(BlockNumberAddress, ChangesetEntry)>> {
) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
let provider = match self.get_segment_provider_for_block(
StaticFileSegment::StorageChangeSets,
block_number,
@@ -2538,10 +2538,7 @@ impl<N: NodePrimitives> StorageChangeSetReader for StaticFileProvider<N> {
for i in offset.changeset_range() {
if let Some(change) = cursor.get_one::<StorageChangesetMask>(i.into())? {
let block_address = BlockNumberAddress((block_number, change.address));
let entry = ChangesetEntry {
key: StorageSlotKey::hashed(change.key),
value: change.value,
};
let entry = StorageEntry { key: change.key, value: change.value };
changeset.push((block_address, entry));
}
}
@@ -2556,7 +2553,7 @@ impl<N: NodePrimitives> StorageChangeSetReader for StaticFileProvider<N> {
block_number: BlockNumber,
address: Address,
storage_key: B256,
) -> ProviderResult<Option<ChangesetEntry>> {
) -> ProviderResult<Option<StorageEntry>> {
let provider = match self.get_segment_provider_for_block(
StaticFileSegment::StorageChangeSets,
block_number,
@@ -2605,10 +2602,7 @@ impl<N: NodePrimitives> StorageChangeSetReader for StaticFileProvider<N> {
.get_one::<StorageChangesetMask>(low.into())?
.filter(|change| change.address == address && change.key == storage_key)
{
return Ok(Some(ChangesetEntry {
key: StorageSlotKey::hashed(change.key),
value: change.value,
}));
return Ok(Some(StorageEntry { key: change.key, value: change.value }));
}
Ok(None)
@@ -2617,7 +2611,7 @@ impl<N: NodePrimitives> StorageChangeSetReader for StaticFileProvider<N> {
fn storage_changesets_range(
&self,
range: impl RangeBounds<BlockNumber>,
) -> ProviderResult<Vec<(BlockNumberAddress, ChangesetEntry)>> {
) -> ProviderResult<Vec<(BlockNumberAddress, StorageEntry)>> {
let range = self.bound_range(range, StaticFileSegment::StorageChangeSets);
self.walk_storage_changeset_range(range).collect()
}

View File

@@ -1170,13 +1170,13 @@ mod tests {
let result = sf_rw.get_storage_before_block(0, test_address, test_key).unwrap();
assert!(result.is_some());
let entry = result.unwrap();
assert_eq!(entry.key.as_b256(), test_key);
assert_eq!(entry.key, test_key);
assert_eq!(entry.value, U256::ZERO);
let result = sf_rw.get_storage_before_block(2, test_address, test_key).unwrap();
assert!(result.is_some());
let entry = result.unwrap();
assert_eq!(entry.key.as_b256(), test_key);
assert_eq!(entry.key, test_key);
assert_eq!(entry.value, U256::from(9));
let result = sf_rw.get_storage_before_block(1, test_address, test_key).unwrap();
@@ -1188,7 +1188,7 @@ mod tests {
let result = sf_rw.get_storage_before_block(1, other_address, other_key).unwrap();
assert!(result.is_some());
let entry = result.unwrap();
assert_eq!(entry.key.as_b256(), other_key);
assert_eq!(entry.key, other_key);
}
}
@@ -1334,20 +1334,20 @@ mod tests {
let result = sf_rw.get_storage_before_block(block_num, address, keys[0]).unwrap();
assert!(result.is_some());
let entry = result.unwrap();
assert_eq!(entry.key.as_b256(), keys[0]);
assert_eq!(entry.key, keys[0]);
assert_eq!(entry.value, U256::from(0));
let result =
sf_rw.get_storage_before_block(block_num, address, keys[num_slots - 1]).unwrap();
assert!(result.is_some());
let entry = result.unwrap();
assert_eq!(entry.key.as_b256(), keys[num_slots - 1]);
assert_eq!(entry.key, keys[num_slots - 1]);
let mid = num_slots / 2;
let result = sf_rw.get_storage_before_block(block_num, address, keys[mid]).unwrap();
assert!(result.is_some());
let entry = result.unwrap();
assert_eq!(entry.key.as_b256(), keys[mid]);
assert_eq!(entry.key, keys[mid]);
let missing_key = B256::with_last_byte(255);
let result = sf_rw.get_storage_before_block(block_num, address, missing_key).unwrap();
@@ -1356,7 +1356,7 @@ mod tests {
for i in (0..num_slots).step_by(10) {
let result = sf_rw.get_storage_before_block(block_num, address, keys[i]).unwrap();
assert!(result.is_some());
assert_eq!(result.unwrap().key.as_b256(), keys[i]);
assert_eq!(result.unwrap().key, keys[i]);
}
}
}

View File

@@ -22,20 +22,20 @@ use reth_chainspec::{ChainInfo, EthChainSpec};
use reth_db::transaction::DbTx;
use reth_db_api::{
mock::{DatabaseMock, TxMock},
models::{AccountBeforeTx, StorageSettings, StoredBlockBodyIndices},
models::{AccountBeforeTx, StoredBlockBodyIndices},
};
use reth_ethereum_primitives::EthPrimitives;
use reth_execution_types::ExecutionOutcome;
use reth_primitives_traits::{
Account, Block, BlockBody, Bytecode, GotExpected, NodePrimitives, RecoveredBlock, SealedHeader,
SignerRecoverable,
SignerRecoverable, StorageEntry,
};
use reth_prune_types::{PruneCheckpoint, PruneModes, PruneSegment};
use reth_stages_types::{StageCheckpoint, StageId};
use reth_storage_api::{
BlockBodyIndicesProvider, BytecodeReader, ChangesetEntry, DBProvider, DatabaseProviderFactory,
BlockBodyIndicesProvider, BytecodeReader, DBProvider, DatabaseProviderFactory,
HashedPostStateProvider, NodePrimitivesProvider, StageCheckpointReader, StateProofProvider,
StorageChangeSetReader, StorageRootProvider, StorageSettingsCache,
StorageChangeSetReader, StorageRootProvider,
};
use reth_storage_errors::provider::{ConsistentViewError, ProviderError, ProviderResult};
use reth_trie::{
@@ -883,14 +883,6 @@ where
let lock = self.accounts.lock();
Ok(lock.get(&account).and_then(|account| account.storage.get(&storage_key)).copied())
}
fn storage_by_hashed_key(
&self,
_address: Address,
_hashed_storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
Ok(None)
}
}
impl<T, ChainSpec> BytecodeReader for MockEthProvider<T, ChainSpec>
@@ -911,16 +903,6 @@ where
}
}
impl<T: NodePrimitives, ChainSpec: Send + Sync> StorageSettingsCache
for MockEthProvider<T, ChainSpec>
{
fn cached_storage_settings(&self) -> StorageSettings {
StorageSettings::default()
}
fn set_storage_settings_cache(&self, _settings: StorageSettings) {}
}
impl<T: NodePrimitives, ChainSpec: EthChainSpec + Send + Sync + 'static> StateProviderFactory
for MockEthProvider<T, ChainSpec>
{
@@ -1029,7 +1011,7 @@ impl<T: NodePrimitives, ChainSpec: Send + Sync> StorageChangeSetReader
fn storage_changeset(
&self,
_block_number: BlockNumber,
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, ChangesetEntry)>> {
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, StorageEntry)>> {
Ok(Vec::default())
}
@@ -1038,14 +1020,14 @@ impl<T: NodePrimitives, ChainSpec: Send + Sync> StorageChangeSetReader
_block_number: BlockNumber,
_address: Address,
_storage_key: B256,
) -> ProviderResult<Option<ChangesetEntry>> {
) -> ProviderResult<Option<StorageEntry>> {
Ok(None)
}
fn storage_changesets_range(
&self,
_range: impl RangeBounds<BlockNumber>,
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, ChangesetEntry)>> {
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, StorageEntry)>> {
Ok(Vec::default())
}

View File

@@ -10,7 +10,7 @@ use reth_chain_state::{
CanonStateSubscriptions, ForkChoiceSubscriptions, PersistedBlockSubscriptions,
};
use reth_node_types::{BlockTy, HeaderTy, NodeTypesWithDB, ReceiptTy, TxTy};
use reth_storage_api::{NodePrimitivesProvider, StorageChangeSetReader, StorageSettingsCache};
use reth_storage_api::{NodePrimitivesProvider, StorageChangeSetReader};
use std::fmt::Debug;
/// Helper trait to unify all provider traits for simplicity.
@@ -21,8 +21,7 @@ pub trait FullProvider<N: NodeTypesWithDB>:
+ StageCheckpointReader
+ PruneCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache,
+ StorageChangeSetReader,
> + NodePrimitivesProvider<Primitives = N::Primitives>
+ StaticFileProviderFactory<Primitives = N::Primitives>
+ RocksDBProviderFactory
@@ -56,8 +55,7 @@ impl<T, N: NodeTypesWithDB> FullProvider<N> for T where
+ StageCheckpointReader
+ PruneCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache,
+ StorageChangeSetReader,
> + NodePrimitivesProvider<Primitives = N::Primitives>
+ StaticFileProviderFactory<Primitives = N::Primitives>
+ RocksDBProviderFactory

View File

@@ -1081,14 +1081,6 @@ where
})
}
fn storage_by_hashed_key(
&self,
_address: Address,
_hashed_storage_key: StorageKey,
) -> Result<Option<U256>, ProviderError> {
Err(ProviderError::UnsupportedProvider)
}
fn account_code(&self, addr: &Address) -> Result<Option<Bytecode>, ProviderError> {
self.block_on_async(async {
let code = self

View File

@@ -1,4 +1,3 @@
use crate::ChangesetEntry;
use alloc::collections::{BTreeMap, BTreeSet};
use alloy_primitives::{map::B256Map, Address, BlockNumber, B256};
use auto_impl::auto_impl;
@@ -48,7 +47,7 @@ pub trait HashingWriter: Send {
/// Mapping of hashed keys of updated accounts to their respective updated hashed slots.
fn unwind_storage_hashing(
&self,
changesets: impl Iterator<Item = (BlockNumberAddress, ChangesetEntry)>,
changesets: impl Iterator<Item = (BlockNumberAddress, StorageEntry)>,
) -> ProviderResult<B256Map<BTreeSet<B256>>>;
/// Unwind and clear storage hashing in a given block range.

View File

@@ -1,9 +1,9 @@
use crate::ChangesetEntry;
use alloy_primitives::{Address, BlockNumber, B256};
use auto_impl::auto_impl;
use core::ops::{RangeBounds, RangeInclusive};
use reth_db_api::models::BlockNumberAddress;
use reth_db_models::AccountBeforeTx;
use reth_primitives_traits::StorageEntry;
use reth_storage_errors::provider::ProviderResult;
/// History Writer
@@ -36,7 +36,7 @@ pub trait HistoryWriter: Send {
/// Returns number of changesets walked.
fn unwind_storage_history_indices(
&self,
changesets: impl Iterator<Item = (BlockNumberAddress, ChangesetEntry)>,
changesets: impl Iterator<Item = (BlockNumberAddress, StorageEntry)>,
) -> ProviderResult<usize>;
/// Unwind and clear storage history indices in a given block range.

View File

@@ -41,7 +41,6 @@ macro_rules! delegate_provider_impls {
}
StateProvider $(where [$($generics)*])? {
fn storage(&self, account: alloy_primitives::Address, storage_key: alloy_primitives::StorageKey) -> reth_storage_api::errors::provider::ProviderResult<Option<alloy_primitives::StorageValue>>;
fn storage_by_hashed_key(&self, address: alloy_primitives::Address, hashed_storage_key: alloy_primitives::StorageKey) -> reth_storage_api::errors::provider::ProviderResult<Option<alloy_primitives::StorageValue>>;
}
BytecodeReader $(where [$($generics)*])? {
fn bytecode_by_hash(&self, code_hash: &alloy_primitives::B256) -> reth_storage_api::errors::provider::ProviderResult<Option<reth_primitives_traits::Bytecode>>;

View File

@@ -10,7 +10,7 @@ use crate::{
};
#[cfg(feature = "db-api")]
use crate::{DBProvider, DatabaseProviderFactory, StorageChangeSetReader, StorageSettingsCache};
use crate::{DBProvider, DatabaseProviderFactory, StorageChangeSetReader};
use alloc::{boxed::Box, string::String, sync::Arc, vec::Vec};
use alloy_consensus::transaction::TransactionMeta;
use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumberOrTag};
@@ -413,7 +413,9 @@ impl<C: Send + Sync, N: NodePrimitives> StorageChangeSetReader for NoopProvider<
fn storage_changeset(
&self,
_block_number: BlockNumber,
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, crate::ChangesetEntry)>> {
) -> ProviderResult<
Vec<(reth_db_api::models::BlockNumberAddress, reth_primitives_traits::StorageEntry)>,
> {
Ok(Vec::default())
}
@@ -422,14 +424,16 @@ impl<C: Send + Sync, N: NodePrimitives> StorageChangeSetReader for NoopProvider<
_block_number: BlockNumber,
_address: Address,
_storage_key: B256,
) -> ProviderResult<Option<crate::ChangesetEntry>> {
) -> ProviderResult<Option<reth_primitives_traits::StorageEntry>> {
Ok(None)
}
fn storage_changesets_range(
&self,
_range: impl core::ops::RangeBounds<BlockNumber>,
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, crate::ChangesetEntry)>> {
) -> ProviderResult<
Vec<(reth_db_api::models::BlockNumberAddress, reth_primitives_traits::StorageEntry)>,
> {
Ok(Vec::default())
}
@@ -538,14 +542,6 @@ impl<C: Send + Sync, N: NodePrimitives> StateProvider for NoopProvider<C, N> {
) -> ProviderResult<Option<StorageValue>> {
Ok(None)
}
fn storage_by_hashed_key(
&self,
_account: Address,
_hashed_storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>> {
Err(ProviderError::UnsupportedProvider)
}
}
impl<C: Send + Sync, N: NodePrimitives> BytecodeReader for NoopProvider<C, N> {
@@ -699,12 +695,3 @@ impl<ChainSpec: Send + Sync, N: NodePrimitives> DatabaseProviderFactory
Ok(self.clone())
}
}
#[cfg(feature = "db-api")]
impl<ChainSpec: Send + Sync, N: Send + Sync> StorageSettingsCache for NoopProvider<ChainSpec, N> {
fn cached_storage_settings(&self) -> reth_db_api::models::StorageSettings {
reth_db_api::models::StorageSettings::default()
}
fn set_storage_settings_cache(&self, _settings: reth_db_api::models::StorageSettings) {}
}

View File

@@ -41,27 +41,12 @@ pub trait StateProvider:
+ HashedPostStateProvider
{
/// Get storage of given account.
///
/// When `use_hashed_state` is enabled, the `account` and `storage_key` are hashed internally
/// before lookup. Callers must pass **unhashed** (plain) values.
fn storage(
&self,
account: Address,
storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>>;
/// Get storage using a pre-hashed storage key.
///
/// Unlike [`Self::storage`], `hashed_storage_key` must already be keccak256-hashed.
/// The `address` remains unhashed (plain) since history indices are keyed by plain address.
/// This is used when changeset keys are pre-hashed (e.g., `use_hashed_state` mode)
/// to avoid double-hashing.
fn storage_by_hashed_key(
&self,
address: Address,
hashed_storage_key: StorageKey,
) -> ProviderResult<Option<StorageValue>>;
/// Get account code by its address.
///
/// Returns `None` if the account doesn't exist or account is not a contract

View File

@@ -2,38 +2,11 @@ use alloc::{
collections::{BTreeMap, BTreeSet},
vec::Vec,
};
use alloy_primitives::{Address, BlockNumber, B256, U256};
use alloy_primitives::{Address, BlockNumber, B256};
use core::ops::{RangeBounds, RangeInclusive};
use reth_primitives_traits::{StorageEntry, StorageSlotKey};
use reth_primitives_traits::StorageEntry;
use reth_storage_errors::provider::ProviderResult;
/// A storage changeset entry whose key is tagged as [`StorageSlotKey::Plain`] or
/// [`StorageSlotKey::Hashed`] by the reader that produced it.
///
/// Unlike [`StorageEntry`] (the raw DB row type with an untagged `B256` key),
/// this type carries provenance so downstream code can call
/// [`StorageSlotKey::to_hashed`] without consulting `StorageSettings`.
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct ChangesetEntry {
/// Storage slot key, tagged with its hashing status.
pub key: StorageSlotKey,
/// Value at this storage slot before the change.
pub value: U256,
}
impl ChangesetEntry {
/// Convert to a raw [`StorageEntry`] (drops the tag).
pub const fn into_storage_entry(self) -> StorageEntry {
StorageEntry { key: self.key.as_b256(), value: self.value }
}
}
impl From<ChangesetEntry> for StorageEntry {
fn from(e: ChangesetEntry) -> Self {
e.into_storage_entry()
}
}
/// Storage reader
#[auto_impl::auto_impl(&, Box)]
pub trait StorageReader: Send {
@@ -64,35 +37,26 @@ pub trait StorageReader: Send {
#[auto_impl::auto_impl(&, Box)]
pub trait StorageChangeSetReader: Send {
/// Iterate over storage changesets and return the storage state from before this block.
///
/// Returned entries have their keys tagged as [`StorageSlotKey::Plain`] or
/// [`StorageSlotKey::Hashed`] based on the current storage mode.
fn storage_changeset(
&self,
block_number: BlockNumber,
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, ChangesetEntry)>>;
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, StorageEntry)>>;
/// Search the block's changesets for the given address and storage key, and return the result.
///
/// The `storage_key` must match the key format used by the storage mode
/// (plain in v1, keccak256-hashed in v2).
///
/// Returns `None` if the storage slot was not changed in this block.
fn get_storage_before_block(
&self,
block_number: BlockNumber,
address: Address,
storage_key: B256,
) -> ProviderResult<Option<ChangesetEntry>>;
) -> ProviderResult<Option<StorageEntry>>;
/// Get all storage changesets in a range of blocks.
///
/// Returned entries have their keys tagged as [`StorageSlotKey::Plain`] or
/// [`StorageSlotKey::Hashed`] based on the current storage mode.
fn storage_changesets_range(
&self,
range: impl RangeBounds<BlockNumber>,
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, ChangesetEntry)>>;
) -> ProviderResult<Vec<(reth_db_api::models::BlockNumberAddress, StorageEntry)>>;
/// Get the total count of all storage changes.
fn storage_changeset_count(&self) -> ProviderResult<usize>;
@@ -109,7 +73,7 @@ pub trait StorageChangeSetReader: Send {
.into_iter()
.map(|(block_address, entry)| reth_db_models::StorageBeforeTx {
address: block_address.address(),
key: entry.key.as_b256(),
key: entry.key,
value: entry.value,
})
.collect()

View File

@@ -52,7 +52,20 @@ impl HashedPostState {
) -> Self {
state
.into_par_iter()
.map(|(address, account)| Self::hash_bundle_account::<KH>(address, account))
.map(|(address, account)| {
let hashed_address = KH::hash_key(address);
let hashed_account = account.info.as_ref().map(Into::into);
let hashed_storage = HashedStorage::from_plain_storage(
account.status,
account.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
);
(
hashed_address,
hashed_account,
(!hashed_storage.is_empty()).then_some(hashed_storage),
)
})
.collect()
}
@@ -65,57 +78,23 @@ impl HashedPostState {
) -> Self {
state
.into_iter()
.map(|(address, account)| Self::hash_bundle_account::<KH>(address, account))
.map(|(address, account)| {
let hashed_address = KH::hash_key(address);
let hashed_account = account.info.as_ref().map(Into::into);
let hashed_storage = HashedStorage::from_plain_storage(
account.status,
account.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
);
(
hashed_address,
hashed_account,
(!hashed_storage.is_empty()).then_some(hashed_storage),
)
})
.collect()
}
/// Initialize [`HashedPostState`] from bundle state, choosing between sequential and parallel
/// hashing based on the number of accounts.
///
/// For small account counts (≤ [`Self::ADAPTIVE_THRESHOLD`]), sequential iteration avoids
/// rayon's fixed scheduling overhead, which can exceed the hashing work itself.
#[cfg(feature = "rayon")]
pub fn from_bundle_state_adaptive<KH: KeyHasher>(
state: &HashMap<Address, BundleAccount>,
) -> Self {
if state.len() <= Self::ADAPTIVE_THRESHOLD {
state
.iter()
.map(|(address, account)| Self::hash_bundle_account::<KH>(address, account))
.collect()
} else {
let entries: Vec<_> = state.iter().collect();
Self::from_bundle_state::<KH>(entries)
}
}
/// Initialize [`HashedPostState`] from bundle state sequentially (non-rayon fallback).
#[cfg(not(feature = "rayon"))]
pub fn from_bundle_state_adaptive<KH: KeyHasher>(
state: &HashMap<Address, BundleAccount>,
) -> Self {
Self::from_bundle_state::<KH>(state)
}
/// Account count at or below which sequential hashing outperforms rayon parallel iteration.
#[cfg(feature = "rayon")]
pub const ADAPTIVE_THRESHOLD: usize = 32;
/// Hashes a single bundle account entry into the tuple expected by [`FromIterator`].
fn hash_bundle_account<KH: KeyHasher>(
address: &Address,
account: &BundleAccount,
) -> (B256, Option<Account>, Option<HashedStorage>) {
let hashed_address = KH::hash_key(address);
let hashed_account = account.info.as_ref().map(Into::into);
let hashed_storage = HashedStorage::from_plain_storage(
account.status,
account.storage.iter().map(|(slot, value)| (slot, &value.present_value)),
);
(hashed_address, hashed_account, (!hashed_storage.is_empty()).then_some(hashed_storage))
}
/// Construct [`HashedPostState`] from a single [`HashedStorage`].
pub fn from_hashed_storage(hashed_address: B256, storage: HashedStorage) -> Self {
Self {

View File

@@ -16,20 +16,3 @@ impl KeyHasher for KeccakKeyHasher {
keccak256(bytes)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keccak_key_hasher_always_hashes_regardless_of_length() {
use alloy_primitives::Address;
let addr = Address::repeat_byte(0x42);
assert_eq!(KeccakKeyHasher::hash_key(addr), keccak256(addr));
let slot = B256::repeat_byte(0x42);
assert_eq!(KeccakKeyHasher::hash_key(slot), keccak256(slot));
assert_ne!(KeccakKeyHasher::hash_key(slot), slot);
}
}

View File

@@ -7,18 +7,17 @@
//! - **Reorg support**: Quickly access changesets to revert blocks during chain reorganizations
//! - **Memory efficiency**: Automatic eviction ensures bounded memory usage
use crate::{DatabaseStateRoot, DatabaseTrieCursorFactory};
use crate::{DatabaseHashedPostState, DatabaseStateRoot, DatabaseTrieCursorFactory};
use alloy_primitives::{map::B256Map, BlockNumber, B256};
use parking_lot::RwLock;
use reth_storage_api::{
BlockNumReader, ChangeSetReader, DBProvider, StageCheckpointReader, StorageChangeSetReader,
StorageSettingsCache,
};
use reth_storage_errors::provider::{ProviderError, ProviderResult};
use reth_trie::{
changesets::compute_trie_changesets,
trie_cursor::{InMemoryTrieCursorFactory, TrieCursor, TrieCursorFactory},
StateRoot, TrieInputSorted,
HashedPostStateSorted, KeccakKeyHasher, StateRoot, TrieInputSorted,
};
use reth_trie_common::updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted};
use std::{collections::BTreeMap, ops::RangeInclusive, sync::Arc, time::Instant};
@@ -67,8 +66,7 @@ where
+ StageCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ StorageSettingsCache,
+ BlockNumReader,
{
debug!(
target: "trie::changeset_cache",
@@ -79,11 +77,14 @@ where
// Step 1: Collect/calculate state reverts
// This is just the changes from this specific block
let individual_state_revert =
crate::state::from_reverts_auto(provider, block_number..=block_number)?;
let individual_state_revert = HashedPostStateSorted::from_reverts::<KeccakKeyHasher>(
provider,
block_number..=block_number,
)?;
// This reverts all changes from db tip back to just after block was processed
let cumulative_state_revert = crate::state::from_reverts_auto(provider, (block_number + 1)..)?;
let cumulative_state_revert =
HashedPostStateSorted::from_reverts::<KeccakKeyHasher>(provider, (block_number + 1)..)?;
// This reverts all changes from db tip back to just after block-1 was processed
let mut cumulative_state_revert_prev = cumulative_state_revert.clone();
@@ -179,8 +180,7 @@ where
+ StageCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ StorageSettingsCache,
+ BlockNumReader,
{
let tx = provider.tx_ref();
@@ -334,8 +334,7 @@ impl ChangesetCache {
+ StageCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ StorageSettingsCache,
+ BlockNumReader,
{
// Try cache first (with read lock)
{
@@ -424,8 +423,7 @@ impl ChangesetCache {
+ StageCheckpointReader
+ ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ StorageSettingsCache,
+ BlockNumReader,
{
// Get the database tip block number
let db_tip_block = provider

View File

@@ -17,7 +17,7 @@ pub use hashed_cursor::{
};
pub use prefix_set::load_prefix_sets_with_provider;
pub use proof::{DatabaseProof, DatabaseStorageProof};
pub use state::{from_reverts_auto, DatabaseHashedPostState, DatabaseStateRoot};
pub use state::{DatabaseHashedPostState, DatabaseStateRoot};
pub use storage::{hashed_storage_from_reverts_with_provider, DatabaseStorageRoot};
pub use trie_cursor::{
DatabaseAccountTrieCursor, DatabaseStorageTrieCursor, DatabaseTrieCursorFactory,

View File

@@ -1,5 +1,4 @@
use alloy_primitives::{
keccak256,
map::{HashMap, HashSet},
BlockNumber, B256,
};
@@ -10,26 +9,23 @@ use reth_db_api::{
tables,
transaction::DbTx,
};
use reth_primitives_traits::StorageEntry;
use reth_storage_api::{ChangeSetReader, DBProvider, StorageChangeSetReader};
use reth_storage_errors::provider::ProviderError;
use reth_trie::{
prefix_set::{PrefixSetMut, TriePrefixSets},
Nibbles,
KeyHasher, Nibbles,
};
/// Load prefix sets using a provider that implements [`ChangeSetReader`]. This function can read
/// changesets from both static files and database.
///
/// Storage keys from changesets are tagged as
/// [`Plain`](reth_primitives_traits::StorageSlotKey::Plain)
/// or [`Hashed`](reth_primitives_traits::StorageSlotKey::Hashed) by the reader, so callers need
/// not pass a `use_hashed_state` flag. Addresses are always hashed.
pub fn load_prefix_sets_with_provider<Provider>(
pub fn load_prefix_sets_with_provider<Provider, KH>(
provider: &Provider,
range: RangeInclusive<BlockNumber>,
) -> Result<TriePrefixSets, ProviderError>
where
Provider: ChangeSetReader + StorageChangeSetReader + DBProvider,
KH: KeyHasher,
{
let tx = provider.tx_ref();
@@ -45,7 +41,7 @@ where
let mut account_hashed_state_cursor = tx.cursor_read::<tables::HashedAccounts>()?;
for (_, AccountBeforeTx { address, .. }) in account_changesets {
let hashed_address = keccak256(address);
let hashed_address = KH::hash_key(address);
account_prefix_set.insert(Nibbles::unpack(hashed_address));
if account_hashed_state_cursor.seek_exact(hashed_address)?.is_none() {
@@ -55,13 +51,13 @@ where
// Walk storage changesets using the provider (handles static files + database)
let storage_changesets = provider.storage_changesets_range(range)?;
for (BlockNumberAddress((_, address)), storage_entry) in storage_changesets {
let hashed_address = keccak256(address);
for (BlockNumberAddress((_, address)), StorageEntry { key, .. }) in storage_changesets {
let hashed_address = KH::hash_key(address);
account_prefix_set.insert(Nibbles::unpack(hashed_address));
storage_prefix_sets
.entry(hashed_address)
.or_default()
.insert(Nibbles::unpack(storage_entry.key.to_hashed()));
.insert(Nibbles::unpack(KH::hash_key(key)));
}
Ok(TriePrefixSets {

View File

@@ -1,18 +1,18 @@
use crate::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory};
use alloy_primitives::{keccak256, map::B256Map, BlockNumber, B256};
use crate::{
load_prefix_sets_with_provider, DatabaseHashedCursorFactory, DatabaseTrieCursorFactory,
};
use alloy_primitives::{map::B256Map, BlockNumber, B256};
use reth_db_api::{
models::{AccountBeforeTx, BlockNumberAddress},
transaction::DbTx,
};
use reth_execution_errors::StateRootError;
use reth_storage_api::{
BlockNumReader, ChangeSetReader, DBProvider, StorageChangeSetReader, StorageSettingsCache,
};
use reth_storage_api::{BlockNumReader, ChangeSetReader, DBProvider, StorageChangeSetReader};
use reth_storage_errors::provider::ProviderError;
use reth_trie::{
hashed_cursor::HashedPostStateCursorFactory, trie_cursor::InMemoryTrieCursorFactory,
updates::TrieUpdates, HashedPostStateSorted, HashedStorageSorted, StateRoot, StateRootProgress,
TrieInputSorted,
updates::TrieUpdates, HashedPostStateSorted, HashedStorageSorted, KeccakKeyHasher, KeyHasher,
StateRoot, StateRootProgress, TrieInputSorted,
};
use std::{
collections::HashSet,
@@ -32,10 +32,7 @@ pub trait DatabaseStateRoot<'a, TX>: Sized {
///
/// An instance of state root calculator with account and storage prefixes loaded.
fn incremental_root_calculator(
provider: &'a (impl ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ DBProvider<Tx = TX>),
provider: &'a (impl ChangeSetReader + StorageChangeSetReader + DBProvider<Tx = TX>),
range: RangeInclusive<BlockNumber>,
) -> Result<Self, StateRootError>;
@@ -46,10 +43,7 @@ pub trait DatabaseStateRoot<'a, TX>: Sized {
///
/// The updated state root.
fn incremental_root(
provider: &'a (impl ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ DBProvider<Tx = TX>),
provider: &'a (impl ChangeSetReader + StorageChangeSetReader + DBProvider<Tx = TX>),
range: RangeInclusive<BlockNumber>,
) -> Result<B256, StateRootError>;
@@ -62,10 +56,7 @@ pub trait DatabaseStateRoot<'a, TX>: Sized {
///
/// The updated state root and the trie updates.
fn incremental_root_with_updates(
provider: &'a (impl ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ DBProvider<Tx = TX>),
provider: &'a (impl ChangeSetReader + StorageChangeSetReader + DBProvider<Tx = TX>),
range: RangeInclusive<BlockNumber>,
) -> Result<(B256, TrieUpdates), StateRootError>;
@@ -76,10 +67,7 @@ pub trait DatabaseStateRoot<'a, TX>: Sized {
///
/// The intermediate progress of state root computation.
fn incremental_root_with_progress(
provider: &'a (impl ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ DBProvider<Tx = TX>),
provider: &'a (impl ChangeSetReader + StorageChangeSetReader + DBProvider<Tx = TX>),
range: RangeInclusive<BlockNumber>,
) -> Result<StateRootProgress, StateRootError>;
@@ -142,12 +130,7 @@ pub trait DatabaseStateRoot<'a, TX>: Sized {
pub trait DatabaseHashedPostState: Sized {
/// Initializes [`HashedPostStateSorted`] from reverts. Iterates over state reverts in the
/// specified range and aggregates them into sorted hashed state.
///
/// Storage keys from changesets are tagged as
/// [`Plain`](reth_primitives_traits::StorageSlotKey::Plain) or
/// [`Hashed`](reth_primitives_traits::StorageSlotKey::Hashed) by the reader, so no
/// `use_hashed_state` flag is needed. Addresses are always hashed.
fn from_reverts(
fn from_reverts<KH: KeyHasher>(
provider: &(impl ChangeSetReader + StorageChangeSetReader + BlockNumReader + DBProvider),
range: impl RangeBounds<BlockNumber>,
) -> Result<HashedPostStateSorted, ProviderError>;
@@ -161,22 +144,16 @@ impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX>
}
fn incremental_root_calculator(
provider: &'a (impl ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ DBProvider<Tx = TX>),
provider: &'a (impl ChangeSetReader + StorageChangeSetReader + DBProvider<Tx = TX>),
range: RangeInclusive<BlockNumber>,
) -> Result<Self, StateRootError> {
let loaded_prefix_sets =
crate::prefix_set::load_prefix_sets_with_provider(provider, range)?;
load_prefix_sets_with_provider::<_, KeccakKeyHasher>(provider, range)?;
Ok(Self::from_tx(provider.tx_ref()).with_prefix_sets(loaded_prefix_sets))
}
fn incremental_root(
provider: &'a (impl ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ DBProvider<Tx = TX>),
provider: &'a (impl ChangeSetReader + StorageChangeSetReader + DBProvider<Tx = TX>),
range: RangeInclusive<BlockNumber>,
) -> Result<B256, StateRootError> {
debug!(target: "trie::loader", ?range, "incremental state root");
@@ -184,10 +161,7 @@ impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX>
}
fn incremental_root_with_updates(
provider: &'a (impl ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ DBProvider<Tx = TX>),
provider: &'a (impl ChangeSetReader + StorageChangeSetReader + DBProvider<Tx = TX>),
range: RangeInclusive<BlockNumber>,
) -> Result<(B256, TrieUpdates), StateRootError> {
debug!(target: "trie::loader", ?range, "incremental state root");
@@ -195,10 +169,7 @@ impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX>
}
fn incremental_root_with_progress(
provider: &'a (impl ChangeSetReader
+ StorageChangeSetReader
+ StorageSettingsCache
+ DBProvider<Tx = TX>),
provider: &'a (impl ChangeSetReader + StorageChangeSetReader + DBProvider<Tx = TX>),
range: RangeInclusive<BlockNumber>,
) -> Result<StateRootProgress, StateRootError> {
debug!(target: "trie::loader", ?range, "incremental state root with progress");
@@ -265,21 +236,6 @@ impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX>
}
}
/// Calls [`HashedPostStateSorted::from_reverts`].
///
/// This is a convenience wrapper kept for backward compatibility. The storage
/// key tagging is now handled internally by the changeset reader.
pub fn from_reverts_auto(
provider: &(impl ChangeSetReader
+ StorageChangeSetReader
+ BlockNumReader
+ DBProvider
+ StorageSettingsCache),
range: impl RangeBounds<BlockNumber>,
) -> Result<HashedPostStateSorted, ProviderError> {
HashedPostStateSorted::from_reverts(provider, range)
}
impl DatabaseHashedPostState for HashedPostStateSorted {
/// Builds a sorted hashed post-state from reverts.
///
@@ -287,12 +243,9 @@ impl DatabaseHashedPostState for HashedPostStateSorted {
/// This avoids intermediate `HashMap` allocations since MDBX data is already sorted.
///
/// - Reads the first occurrence of each changed account/storage slot in the range.
/// - Addresses are always keccak256-hashed.
/// - Storage keys are tagged by the changeset reader and hashed via
/// [`StorageSlotKey::to_hashed`](reth_primitives_traits::StorageSlotKey::to_hashed).
/// - Returns keys already ordered for trie iteration.
/// - Hashes keys and returns them already ordered for trie iteration.
#[instrument(target = "trie::db", skip(provider), fields(range))]
fn from_reverts(
fn from_reverts<KH: KeyHasher>(
provider: &(impl ChangeSetReader + StorageChangeSetReader + BlockNumReader + DBProvider),
range: impl RangeBounds<BlockNumber>,
) -> Result<Self, ProviderError> {
@@ -315,7 +268,7 @@ impl DatabaseHashedPostState for HashedPostStateSorted {
for entry in provider.account_changesets_range(start..end)? {
let (_, AccountBeforeTx { address, info }) = entry;
if seen_accounts.insert(address) {
accounts.push((keccak256(address), info));
accounts.push((KH::hash_key(address), info));
}
}
accounts.sort_unstable_by_key(|(hash, _)| *hash);
@@ -330,12 +283,12 @@ impl DatabaseHashedPostState for HashedPostStateSorted {
for (BlockNumberAddress((_, address)), storage) in
provider.storage_changesets_range(start..=end_inclusive)?
{
if seen_storage_keys.insert((address, storage.key.as_b256())) {
let hashed_address = keccak256(address);
if seen_storage_keys.insert((address, storage.key)) {
let hashed_address = KH::hash_key(address);
storages
.entry(hashed_address)
.or_default()
.push((storage.key.to_hashed(), storage.value));
.push((KH::hash_key(storage.key), storage.value));
}
}
}
@@ -356,7 +309,7 @@ impl DatabaseHashedPostState for HashedPostStateSorted {
#[cfg(test)]
mod tests {
use super::*;
use alloy_primitives::{hex, keccak256, map::HashMap, Address, B256, U256};
use alloy_primitives::{hex, map::HashMap, Address, B256, U256};
use reth_db::test_utils::create_test_rw_db;
use reth_db_api::{
database::Database,
@@ -485,11 +438,12 @@ mod tests {
)
.unwrap();
let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=3).unwrap();
let sorted =
HashedPostStateSorted::from_reverts::<KeccakKeyHasher>(&*provider, 1..=3).unwrap();
// Verify first occurrences were kept (nonce 1, not 2)
assert_eq!(sorted.accounts.len(), 2);
let hashed_addr1 = keccak256(address1);
let hashed_addr1 = KeccakKeyHasher::hash_key(address1);
let account1 = sorted.accounts.iter().find(|(addr, _)| *addr == hashed_addr1).unwrap();
assert_eq!(account1.1.unwrap().nonce, 1);
@@ -521,225 +475,9 @@ mod tests {
.unwrap();
// Query a range with no data
let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=10).unwrap();
let sorted =
HashedPostStateSorted::from_reverts::<KeccakKeyHasher>(&*provider, 1..=10).unwrap();
assert!(sorted.accounts.is_empty());
assert!(sorted.storages.is_empty());
}
#[test]
fn from_reverts_with_hashed_state() {
use reth_db_api::models::StorageBeforeTx;
use reth_provider::{StaticFileProviderFactory, StaticFileSegment, StaticFileWriter};
let factory = create_test_provider_factory();
let mut settings = factory.cached_storage_settings();
settings.use_hashed_state = true;
settings.storage_changesets_in_static_files = true;
factory.set_storage_settings_cache(settings);
let provider = factory.provider_rw().unwrap();
let address1 = Address::with_last_byte(1);
let address2 = Address::with_last_byte(2);
let plain_slot1 = B256::from(U256::from(11));
let plain_slot2 = B256::from(U256::from(22));
let hashed_slot1 = keccak256(plain_slot1);
let hashed_slot2 = keccak256(plain_slot2);
provider
.tx_ref()
.put::<tables::AccountChangeSets>(
1,
AccountBeforeTx {
address: address1,
info: Some(Account { nonce: 1, ..Default::default() }),
},
)
.unwrap();
provider
.tx_ref()
.put::<tables::AccountChangeSets>(
2,
AccountBeforeTx {
address: address1,
info: Some(Account { nonce: 2, ..Default::default() }),
},
)
.unwrap();
provider
.tx_ref()
.put::<tables::AccountChangeSets>(3, AccountBeforeTx { address: address2, info: None })
.unwrap();
{
let sf = factory.static_file_provider();
let mut writer = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
writer.append_storage_changeset(vec![], 0).unwrap();
writer
.append_storage_changeset(
vec![StorageBeforeTx {
address: address1,
key: hashed_slot2,
value: U256::from(200),
}],
1,
)
.unwrap();
writer
.append_storage_changeset(
vec![StorageBeforeTx {
address: address1,
key: hashed_slot1,
value: U256::from(100),
}],
2,
)
.unwrap();
writer
.append_storage_changeset(
vec![StorageBeforeTx {
address: address1,
key: hashed_slot1,
value: U256::from(999),
}],
3,
)
.unwrap();
writer.commit().unwrap();
}
let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=3).unwrap();
assert_eq!(sorted.accounts.len(), 2);
let hashed_addr1 = keccak256(address1);
let hashed_addr2 = keccak256(address2);
let account1 = sorted.accounts.iter().find(|(addr, _)| *addr == hashed_addr1).unwrap();
assert_eq!(account1.1.unwrap().nonce, 1);
let account2 = sorted.accounts.iter().find(|(addr, _)| *addr == hashed_addr2).unwrap();
assert!(account2.1.is_none());
assert!(sorted.accounts.windows(2).all(|w| w[0].0 <= w[1].0));
let storage = sorted.storages.get(&hashed_addr1).expect("storage for address1");
assert_eq!(storage.storage_slots.len(), 2);
let found_slot1 = storage.storage_slots.iter().find(|(k, _)| *k == hashed_slot1).unwrap();
assert_eq!(found_slot1.1, U256::from(100));
let found_slot2 = storage.storage_slots.iter().find(|(k, _)| *k == hashed_slot2).unwrap();
assert_eq!(found_slot2.1, U256::from(200));
assert_ne!(hashed_slot1, plain_slot1);
assert_ne!(hashed_slot2, plain_slot2);
assert!(storage.storage_slots.windows(2).all(|w| w[0].0 <= w[1].0));
}
#[test]
fn from_reverts_legacy_keccak_hashes_all_keys() {
let factory = create_test_provider_factory();
let provider = factory.provider_rw().unwrap();
let address1 = Address::with_last_byte(1);
let address2 = Address::with_last_byte(2);
let plain_slot1 = B256::from(U256::from(11));
let plain_slot2 = B256::from(U256::from(22));
provider
.tx_ref()
.put::<tables::AccountChangeSets>(
1,
AccountBeforeTx {
address: address1,
info: Some(Account { nonce: 10, ..Default::default() }),
},
)
.unwrap();
provider
.tx_ref()
.put::<tables::AccountChangeSets>(
2,
AccountBeforeTx {
address: address2,
info: Some(Account { nonce: 20, ..Default::default() }),
},
)
.unwrap();
provider
.tx_ref()
.put::<tables::AccountChangeSets>(
3,
AccountBeforeTx {
address: address1,
info: Some(Account { nonce: 99, ..Default::default() }),
},
)
.unwrap();
provider
.tx_ref()
.put::<tables::StorageChangeSets>(
BlockNumberAddress((1, address1)),
StorageEntry { key: plain_slot1, value: U256::from(100) },
)
.unwrap();
provider
.tx_ref()
.put::<tables::StorageChangeSets>(
BlockNumberAddress((2, address1)),
StorageEntry { key: plain_slot2, value: U256::from(200) },
)
.unwrap();
provider
.tx_ref()
.put::<tables::StorageChangeSets>(
BlockNumberAddress((3, address2)),
StorageEntry { key: plain_slot1, value: U256::from(300) },
)
.unwrap();
let sorted = HashedPostStateSorted::from_reverts(&*provider, 1..=3).unwrap();
let expected_hashed_addr1 = keccak256(address1);
let expected_hashed_addr2 = keccak256(address2);
assert_eq!(sorted.accounts.len(), 2);
let account1 =
sorted.accounts.iter().find(|(addr, _)| *addr == expected_hashed_addr1).unwrap();
assert_eq!(account1.1.unwrap().nonce, 10);
let account2 =
sorted.accounts.iter().find(|(addr, _)| *addr == expected_hashed_addr2).unwrap();
assert_eq!(account2.1.unwrap().nonce, 20);
assert!(sorted.accounts.windows(2).all(|w| w[0].0 <= w[1].0));
let expected_hashed_slot1 = keccak256(plain_slot1);
let expected_hashed_slot2 = keccak256(plain_slot2);
assert_ne!(expected_hashed_slot1, plain_slot1);
assert_ne!(expected_hashed_slot2, plain_slot2);
let storage1 = sorted.storages.get(&expected_hashed_addr1).expect("storage for address1");
assert_eq!(storage1.storage_slots.len(), 2);
assert!(storage1
.storage_slots
.iter()
.any(|(k, v)| *k == expected_hashed_slot1 && *v == U256::from(100)));
assert!(storage1
.storage_slots
.iter()
.any(|(k, v)| *k == expected_hashed_slot2 && *v == U256::from(200)));
assert!(storage1.storage_slots.windows(2).all(|w| w[0].0 <= w[1].0));
let storage2 = sorted.storages.get(&expected_hashed_addr2).expect("storage for address2");
assert_eq!(storage2.storage_slots.len(), 1);
assert_eq!(storage2.storage_slots[0].0, expected_hashed_slot1);
assert_eq!(storage2.storage_slots[0].1, U256::from(300));
}
}

View File

@@ -47,7 +47,7 @@ where
provider.storage_changesets_range(from..=tip)?
{
if storage_address == address {
let hashed_slot = storage_change.key.to_hashed();
let hashed_slot = keccak256(storage_change.key);
if let hash_map::Entry::Vacant(entry) = storage.storage.entry(hashed_slot) {
entry.insert(storage_change.value);
}
@@ -101,131 +101,3 @@ impl<'a, TX: DbTx> DatabaseStorageRoot<'a, TX>
.root()
}
}
#[cfg(test)]
mod tests {
use super::*;
use alloy_consensus::Header;
use alloy_primitives::U256;
use reth_db_api::{models::BlockNumberAddress, tables, transaction::DbTxMut};
use reth_primitives_traits::StorageEntry;
use reth_provider::{
test_utils::create_test_provider_factory, StaticFileProviderFactory, StaticFileSegment,
StaticFileWriter, StorageSettingsCache,
};
fn append_storage_changesets_to_static_files(
factory: &impl StaticFileProviderFactory<
Primitives: reth_primitives_traits::NodePrimitives<BlockHeader = Header>,
>,
changesets: Vec<(u64, Vec<reth_db_api::models::StorageBeforeTx>)>,
) {
let sf = factory.static_file_provider();
let mut writer = sf.latest_writer(StaticFileSegment::StorageChangeSets).unwrap();
for (block_number, changeset) in changesets {
writer.append_storage_changeset(changeset, block_number).unwrap();
}
writer.commit().unwrap();
}
fn append_headers_to_static_files(
factory: &impl StaticFileProviderFactory<
Primitives: reth_primitives_traits::NodePrimitives<BlockHeader = Header>,
>,
up_to_block: u64,
) {
let sf = factory.static_file_provider();
let mut writer = sf.latest_writer(StaticFileSegment::Headers).unwrap();
let mut header = Header::default();
for num in 0..=up_to_block {
header.number = num;
writer.append_header(&header, &B256::ZERO).unwrap();
}
writer.commit().unwrap();
}
#[test]
fn test_hashed_storage_from_reverts_legacy() {
let factory = create_test_provider_factory();
let provider = factory.provider_rw().unwrap();
assert!(!provider.cached_storage_settings().use_hashed_state);
let address = Address::with_last_byte(42);
let slot1 = B256::from(U256::from(100));
let slot2 = B256::from(U256::from(200));
append_headers_to_static_files(&factory, 5);
provider
.tx_ref()
.put::<tables::StorageChangeSets>(
BlockNumberAddress((1, address)),
StorageEntry { key: slot1, value: U256::from(10) },
)
.unwrap();
provider
.tx_ref()
.put::<tables::StorageChangeSets>(
BlockNumberAddress((2, address)),
StorageEntry { key: slot2, value: U256::from(20) },
)
.unwrap();
provider
.tx_ref()
.put::<tables::StorageChangeSets>(
BlockNumberAddress((3, address)),
StorageEntry { key: slot1, value: U256::from(999) },
)
.unwrap();
let result = hashed_storage_from_reverts_with_provider(&*provider, address, 1).unwrap();
let hashed_slot1 = keccak256(slot1);
let hashed_slot2 = keccak256(slot2);
assert_eq!(result.storage.len(), 2);
assert_eq!(result.storage.get(&hashed_slot1), Some(&U256::from(10)));
assert_eq!(result.storage.get(&hashed_slot2), Some(&U256::from(20)));
}
#[test]
fn test_hashed_storage_from_reverts_hashed_state() {
use reth_db_api::models::StorageBeforeTx;
let factory = create_test_provider_factory();
let mut settings = factory.cached_storage_settings();
settings.use_hashed_state = true;
settings.storage_changesets_in_static_files = true;
factory.set_storage_settings_cache(settings);
let provider = factory.provider_rw().unwrap();
assert!(provider.cached_storage_settings().use_hashed_state);
assert!(provider.cached_storage_settings().storage_changesets_in_static_files);
let address = Address::with_last_byte(42);
let plain_slot1 = B256::from(U256::from(100));
let plain_slot2 = B256::from(U256::from(200));
let hashed_slot1 = keccak256(plain_slot1);
let hashed_slot2 = keccak256(plain_slot2);
append_headers_to_static_files(&factory, 5);
append_storage_changesets_to_static_files(
&factory,
vec![
(0, vec![]),
(1, vec![StorageBeforeTx { address, key: hashed_slot1, value: U256::from(10) }]),
(2, vec![StorageBeforeTx { address, key: hashed_slot2, value: U256::from(20) }]),
(3, vec![StorageBeforeTx { address, key: hashed_slot1, value: U256::from(999) }]),
],
);
let result = hashed_storage_from_reverts_with_provider(&*provider, address, 1).unwrap();
assert_eq!(result.storage.len(), 2);
assert_eq!(result.storage.get(&hashed_slot1), Some(&U256::from(10)));
assert_eq!(result.storage.get(&hashed_slot2), Some(&U256::from(20)));
}
}

View File

@@ -9,7 +9,7 @@ pub fn hash_post_state(c: &mut Criterion) {
let mut group = c.benchmark_group("Hash Post State");
group.sample_size(20);
for size in [10, 20, 32, 50, 100, 1_000, 3_000, 5_000, 10_000] {
for size in [100, 1_000, 3_000, 5_000, 10_000] {
// Too slow.
#[expect(unexpected_cfgs)]
if cfg!(codspeed) && size > 1_000 {
@@ -27,13 +27,6 @@ pub fn hash_post_state(c: &mut Criterion) {
group.bench_function(BenchmarkId::new("parallel hashing", size), |b| {
b.iter(|| HashedPostState::from_bundle_state::<KeccakKeyHasher>(&state))
});
// adaptive
let state_map: alloy_primitives::map::HashMap<Address, BundleAccount> =
state.iter().map(|(k, v)| (*k, v.clone())).collect();
group.bench_function(BenchmarkId::new("adaptive hashing", size), |b| {
b.iter(|| HashedPostState::from_bundle_state_adaptive::<KeccakKeyHasher>(&state_map))
});
}
}

View File

@@ -60,6 +60,10 @@ target "_base_profiling" {
inherits = ["_base"]
platforms = ["linux/amd64"]
}
target "_base_profiling" {
inherits = ["_base"]
platforms = ["linux/amd64"]
}
// Ethereum (reth)
target "ethereum" {

View File

@@ -1,170 +0,0 @@
# op-reth db settings set use_hashed_state
Use hashed state tables (HashedAccounts/HashedStorages) as canonical state
```bash
$ op-reth db settings set use_hashed_state --help
```
```txt
Usage: op-reth db settings set use_hashed_state [OPTIONS] <VALUE>
Arguments:
<VALUE>
[possible values: true, false]
Options:
-h, --help
Print help (see a summary with '-h')
Datadir:
--chain <CHAIN_OR_PATH>
The chain this node is running.
Possible values are either a built-in chain or the path to a chain specification file.
Built-in chains:
optimism, optimism_sepolia, optimism-sepolia, base, base_sepolia, base-sepolia, arena-z, arena-z-sepolia, automata, base-devnet-0-sepolia-dev-0, bob, boba-sepolia, boba, camp-sepolia, celo, creator-chain-testnet-sepolia, cyber, cyber-sepolia, ethernity, ethernity-sepolia, fraxtal, funki, funki-sepolia, hashkeychain, ink, ink-sepolia, lisk, lisk-sepolia, lyra, metal, metal-sepolia, mint, mode, mode-sepolia, oplabs-devnet-0-sepolia-dev-0, orderly, ozean-sepolia, pivotal-sepolia, polynomial, race, race-sepolia, radius_testnet-sepolia, redstone, rehearsal-0-bn-0-rehearsal-0-bn, rehearsal-0-bn-1-rehearsal-0-bn, settlus-mainnet, settlus-sepolia-sepolia, shape, shape-sepolia, silent-data-mainnet, snax, soneium, soneium-minato-sepolia, sseed, swan, swell, tbn, tbn-sepolia, unichain, unichain-sepolia, worldchain, worldchain-sepolia, xterio-eth, zora, zora-sepolia, dev
[default: optimism]
Logging:
--log.stdout.format <FORMAT>
The format to use for logs written to stdout
Possible values:
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
- terminal: Represents terminal-friendly formatting for logs
[default: terminal]
--log.stdout.filter <FILTER>
The filter to use for logs written to stdout
[default: ]
--log.file.format <FORMAT>
The format to use for logs written to the log file
Possible values:
- json: Represents JSON formatting for logs. This format outputs log records as JSON objects, making it suitable for structured logging
- log-fmt: Represents logfmt (key=value) formatting for logs. This format is concise and human-readable, typically used in command-line applications
- terminal: Represents terminal-friendly formatting for logs
[default: terminal]
--log.file.filter <FILTER>
The filter to use for logs written to the log file
[default: debug]
--log.file.directory <PATH>
The path to put log files in
[default: <CACHE_DIR>/logs]
--log.file.name <NAME>
The prefix name of the log files
[default: reth.log]
--log.file.max-size <SIZE>
The maximum size (in MB) of one log file
[default: 200]
--log.file.max-files <COUNT>
The maximum amount of log files that will be stored. If set to 0, background file logging is disabled
[default: 5]
--log.journald
Write logs to journald
--log.journald.filter <FILTER>
The filter to use for logs written to journald
[default: error]
--color <COLOR>
Sets whether or not the formatter emits ANSI terminal escape codes for colors and other text formatting
Possible values:
- always: Colors on
- auto: Auto-detect
- never: Colors off
[default: always]
--logs-otlp[=<URL>]
Enable `Opentelemetry` logs export to an OTLP endpoint.
If no value provided, defaults based on protocol: - HTTP: `http://localhost:4318/v1/logs` - gRPC: `http://localhost:4317`
Example: --logs-otlp=http://collector:4318/v1/logs
[env: OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=]
--logs-otlp.filter <FILTER>
Set a filter directive for the OTLP logs exporter. This controls the verbosity of logs sent to the OTLP endpoint. It follows the same syntax as the `RUST_LOG` environment variable.
Example: --logs-otlp.filter=info,reth=debug
Defaults to INFO if not specified.
[default: info]
Display:
-v, --verbosity...
Set the minimum log level.
-v Errors
-vv Warnings
-vvv Info
-vvvv Debug
-vvvvv Traces (warning: very verbose!)
-q, --quiet
Silence all log output
Tracing:
--tracing-otlp[=<URL>]
Enable `Opentelemetry` tracing export to an OTLP endpoint.
If no value provided, defaults based on protocol: - HTTP: `http://localhost:4318/v1/traces` - gRPC: `http://localhost:4317`
Example: --tracing-otlp=http://collector:4318/v1/traces
[env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=]
--tracing-otlp-protocol <PROTOCOL>
OTLP transport protocol to use for exporting traces and logs.
- `http`: expects endpoint path to end with `/v1/traces` or `/v1/logs` - `grpc`: expects endpoint without a path
Defaults to HTTP if not specified.
Possible values:
- http: HTTP/Protobuf transport, port 4318, requires `/v1/traces` path
- grpc: gRPC transport, port 4317
[env: OTEL_EXPORTER_OTLP_PROTOCOL=]
[default: http]
--tracing-otlp.filter <FILTER>
Set a filter directive for the OTLP tracer. This controls the verbosity of spans and events sent to the OTLP endpoint. It follows the same syntax as the `RUST_LOG` environment variable.
Example: --tracing-otlp.filter=info,reth=debug,hyper_util=off
Defaults to TRACE if not specified.
[default: debug]
--tracing-otlp.sample-ratio <RATIO>
Trace sampling ratio to control the percentage of traces to export.
Valid range: 0.0 to 1.0 - 1.0, default: Sample all traces - 0.01: Sample 1% of traces - 0.0: Disable sampling
Example: --tracing-otlp.sample-ratio=0.0.
[env: OTEL_TRACES_SAMPLER_ARG=]
```

View File

@@ -293,11 +293,6 @@ Networking:
Example: --netrestrict "192.168.0.0/16,10.0.0.0/8"
--enforce-enr-fork-id
Enforce EIP-868 ENR fork ID validation for discovered peers.
When enabled, peers discovered without a confirmed fork ID are not added to the peer set until their fork ID is verified via EIP-868 ENR request. This filters out peers from other networks that pollute the discovery table.
RPC:
--http
Enable the HTTP-RPC server
@@ -1027,9 +1022,6 @@ Engine:
[default: 100]
--engine.disable-sparse-trie-cache-pruning
Fully disable sparse trie cache pruning. When set, the cached sparse trie is preserved without any node pruning or storage trie eviction between blocks. Useful for benchmarking the effects of retaining the full trie cache
--engine.state-root-task-timeout <STATE_ROOT_TASK_TIMEOUT>
Configure the timeout for the state root task before spawning a sequential fallback. If the state root task takes longer than this, a sequential computation starts in parallel and whichever finishes first is used.

View File

@@ -233,11 +233,6 @@ Networking:
Example: --netrestrict "192.168.0.0/16,10.0.0.0/8"
--enforce-enr-fork-id
Enforce EIP-868 ENR fork ID validation for discovered peers.
When enabled, peers discovered without a confirmed fork ID are not added to the peer set until their fork ID is verified via EIP-868 ENR request. This filters out peers from other networks that pollute the discovery table.
Datadir:
--datadir <DATA_DIR>
The path to the data dir for all reth files and subdirectories.

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