diff --git a/.github/assets/hive/expected_failures.yaml b/.github/assets/hive/expected_failures.yaml index f28fd70be03..ae3817cfc3d 100644 --- a/.github/assets/hive/expected_failures.yaml +++ b/.github/assets/hive/expected_failures.yaml @@ -41,11 +41,7 @@ engine-cancun: sync: [] -# https://github.com/ethereum/hive/issues/1277 -engine-auth: - - "JWT Authentication: No time drift, correct secret (Paris) (reth)" - - "JWT Authentication: Negative time drift, within limit, correct secret (Paris) (reth)" - - "JWT Authentication: Positive time drift, within limit, correct secret (Paris) (reth)" +engine-auth: [] # 7702 test - no fix: it’s too expensive to check whether the storage is empty on each creation # 6110 related tests - may start passing when fixtures improve diff --git a/CLAUDE.md b/CLAUDE.md index 99282fbf864..c7a709c6713 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -236,6 +236,85 @@ Common refactoring pattern: - Add trait bounds for flexibility - Enable reuse across different chain types (Ethereum, Optimism) +#### When to Comment + +Write comments that remain valuable after the PR is merged. Future readers won't have PR context - they only see the current code. + +##### ✅ DO: Add Value + +**Explain WHY and non-obvious behavior:** +```rust +// Process must handle allocations atomically to prevent race conditions +// between dealloc on drop and concurrent limit checks +unsafe impl GlobalAlloc for LimitedAllocator { ... } + +// Binary search requires sorted input. Panics on unsorted slices. +fn find_index(items: &[Item], target: &Item) -> Option + +// Timeout set to 5s to match EVM block processing limits +const TRACER_TIMEOUT: Duration = Duration::from_secs(5); +``` + +**Document constraints and assumptions:** +```rust +/// Returns heap size estimate. +/// +/// Note: May undercount shared references (Rc/Arc). For precise +/// accounting, combine with an allocator-based approach. +fn deep_size_of(&self) -> usize +``` + +**Explain complex logic:** +```rust +// We reset limits at task start because tokio reuses threads in +// spawn_blocking pool. Without reset, second task inherits first +// task's allocation count and immediately hits limit. +THREAD_ALLOCATED.with(|allocated| allocated.set(0)); +``` + +##### ❌ DON'T: Describe Changes +```rust +// ❌ BAD - Describes the change, not the code +// Changed from Vec to HashMap for O(1) lookups + +// ✅ GOOD - Explains the decision +// HashMap provides O(1) symbol lookups during trace replay +``` +```rust +// ❌ BAD - PR-specific context +// Fix for issue #234 where memory wasn't freed + +// ✅ GOOD - Documents the actual behavior +// Explicitly drop allocations before limit check to ensure +// accurate accounting +``` +```rust +// ❌ BAD - States the obvious +// Increment counter +counter += 1; + +// ✅ GOOD - Explains non-obvious purpose +// Track allocations across all threads for global limit enforcement +GLOBAL_COUNTER.fetch_add(1, Ordering::SeqCst); +``` + +✅ **Comment when:** +- Non-obvious behavior or edge cases +- Performance trade-offs +- Safety requirements (unsafe blocks must always be documented) +- Limitations or gotchas +- Why simpler alternatives don't work + +❌ **Don't comment when:** +- Code is self-explanatory +- Just restating the code in English +- Describing what changed in this PR + +##### The Test: "Will this make sense in 6 months?" + +Before adding a comment, ask: Would someone reading just the current code (no PR, no history) find this helpful? + + ### Example Contribution Workflow Let's say you want to fix a bug where external IP resolution fails on startup: diff --git a/Cargo.lock b/Cargo.lock index c2605e4caa2..6d6cc200942 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "addr2line" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] - [[package]] name = "adler2" version = "2.0.1" @@ -59,7 +50,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -97,9 +88,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-chains" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7aacbb0ac0f76aaa64d1e1412f778c0574f241e4073b2a3e09c605884c9b90" +checksum = "bf01dd83a1ca5e4807d0ca0223c9615e211ce5db0a9fd1443c2778cacf89b546" dependencies = [ "alloy-primitives", "alloy-rlp", @@ -112,9 +103,9 @@ dependencies = [ [[package]] name = "alloy-consensus" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59094911f05dbff1cf5b29046a00ef26452eccc8d47136d50a47c0cf22f00c85" +checksum = "b9b151e38e42f1586a01369ec52a6934702731d07e8509a7307331b09f6c46dc" dependencies = [ "alloy-eips", "alloy-primitives", @@ -134,14 +125,14 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "alloy-consensus-any" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "903cb8f728107ca27c816546f15be38c688df3c381d7bd1a4a9f215effc1ddb4" +checksum = "6e2d5e8668ef6215efdb7dcca6f22277b4e483a5650e05f5de22b2350971f4b8" dependencies = [ "alloy-consensus", "alloy-eips", @@ -154,9 +145,9 @@ dependencies = [ [[package]] name = "alloy-contract" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03df5cb3b428ac96b386ad64c11d5c6e87a5505682cf1fbd6f8f773e9eda04f6" +checksum = "630288cf4f3a34a8c6bc75c03dce1dbd47833138f65f37d53a1661eafc96b83f" dependencies = [ "alloy-consensus", "alloy-dyn-abi", @@ -171,7 +162,7 @@ dependencies = [ "futures", "futures-util", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -203,7 +194,7 @@ dependencies = [ "crc", "rand 0.8.5", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -232,14 +223,14 @@ dependencies = [ "rand 0.8.5", "serde", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "alloy-eips" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac7f1c9a1ccc7f3e03c36976455751a6166a4f0d2d2c530c3f87dfe7d0cdc836" +checksum = "e5434834adaf64fa20a6fb90877bc1d33214c41b055cc49f82189c98614368cc" dependencies = [ "alloy-eip2124", "alloy-eip2930", @@ -256,15 +247,15 @@ dependencies = [ "ethereum_ssz_derive", "serde", "serde_with", - "sha2 0.10.9", - "thiserror 2.0.16", + "sha2", + "thiserror 2.0.17", ] [[package]] name = "alloy-evm" -version = "0.21.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06a5f67ee74999aa4fe576a83be1996bdf74a30fce3d248bf2007d6fc7dae8aa" +checksum = "dbb19405755c6f94c9bb856f2b1449767074b7e2002e1ab2be0a79b9b28db322" dependencies = [ "alloy-consensus", "alloy-eips", @@ -280,14 +271,14 @@ dependencies = [ "op-alloy-rpc-types-engine", "op-revm", "revm", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "alloy-genesis" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1421f6c9d15e5b86afbfe5865ca84dea3b9f77173a0963c1a2ee4e626320ada9" +checksum = "919a8471cfbed7bcd8cf1197a57dda583ce0e10c6385f6ff4e8b41304b223392" dependencies = [ "alloy-eips", "alloy-primitives", @@ -299,9 +290,9 @@ dependencies = [ [[package]] name = "alloy-hardforks" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "889eb3949b58368a09d4f16931c660275ef5fb08e5fbd4a96573b19c7085c41f" +checksum = "4b16ee6b2c7d39da592d30a5f9607a83f50ee5ec2a2c301746cc81e91891f4ca" dependencies = [ "alloy-chains", "alloy-eip2124", @@ -325,24 +316,24 @@ dependencies = [ [[package]] name = "alloy-json-rpc" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65f763621707fa09cece30b73ecc607eb43fd7a72451fe3b46f645b905086926" +checksum = "d7c69f6c9c68a1287c9d5ff903d0010726934de0dac10989be37b75a29190d55" dependencies = [ "alloy-primitives", "alloy-sol-types", "http", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] [[package]] name = "alloy-network" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f59a869fa4b4c3a7f08b1c8cb79aec61c29febe6e24a24fe0fcfded8a9b5703" +checksum = "8eaf2ae05219e73e0979cb2cf55612aafbab191d130f203079805eaf881cca58" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -361,14 +352,14 @@ dependencies = [ "futures-utils-wasm", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "alloy-network-primitives" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46e9374c667c95c41177602ebe6f6a2edd455193844f011d973d374b65501b38" +checksum = "e58f4f345cef483eab7374f2b6056973c7419ffe8ad35e994b7a7f5d8e0c7ba4" dependencies = [ "alloy-consensus", "alloy-eips", @@ -379,9 +370,9 @@ dependencies = [ [[package]] name = "alloy-op-evm" -version = "0.21.2" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17aaeb600740c181bf29c9f138f9b228d115ea74fa6d0f0343e1952f1a766968" +checksum = "f059cf29d7f15b3e6581ceb6eda06a16d8ed4b55adc02b0677add3fd381db6bb" dependencies = [ "alloy-consensus", "alloy-eips", @@ -392,13 +383,14 @@ dependencies = [ "op-alloy-consensus", "op-revm", "revm", + "thiserror 2.0.17", ] [[package]] name = "alloy-op-hardforks" -version = "0.3.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "599c1d7dfbccb66603cb93fde00980d12848d32fe5e814f50562104a92df6487" +checksum = "af8bb236fc008fd3b83b2792e30ae79617a99ffc4c3f584f0c9b4ce0a2da52de" dependencies = [ "alloy-chains", "alloy-hardforks", @@ -420,9 +412,9 @@ dependencies = [ "const-hex", "derive_more", "foldhash 0.2.0", - "getrandom 0.3.3", + "getrandom 0.3.4", "hashbrown 0.16.0", - "indexmap 2.11.4", + "indexmap 2.12.0", "itoa", "k256", "keccak-asm", @@ -431,7 +423,7 @@ dependencies = [ "proptest-derive 0.6.0", "rand 0.9.2", "ruint", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "sha3", "tiny-keccak", @@ -439,9 +431,9 @@ dependencies = [ [[package]] name = "alloy-provider" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77818b7348bd5486491a5297579dbfe5f706a81f8e1f5976393025f1e22a7c7d" +checksum = "de2597751539b1cc8fe4204e5325f9a9ed83fcacfb212018dfcfa7877e76de21" dependencies = [ "alloy-chains", "alloy-consensus", @@ -475,7 +467,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", "url", @@ -484,9 +476,9 @@ dependencies = [ [[package]] name = "alloy-pubsub" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "249b45103a66c9ad60ad8176b076106d03a2399a37f0ee7b0e03692e6b354cb9" +checksum = "06e45a68423e732900a0c824b8e22237db461b79d2e472dd68b7547c16104427" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -523,14 +515,14 @@ checksum = "64b728d511962dda67c1bc7ea7c03736ec275ed2cf4c35d9585298ac9ccf3b73" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] name = "alloy-rpc-client" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2430d5623e428dd012c6c2156ae40b7fe638d6fca255e3244e0fba51fa698e93" +checksum = "edf8eb8be597cfa8c312934d2566ec4516f066d69164f9212d7a148979fdcfd8" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -554,9 +546,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e131624d08a25cfc40557041e7dc42e1182fa1153e7592d120f769a1edce56" +checksum = "339af7336571dd39ae3a15bde08ae6a647e62f75350bd415832640268af92c06" dependencies = [ "alloy-primitives", "alloy-rpc-types-engine", @@ -567,9 +559,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-admin" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59407723b1850ebaa49e46d10c2ba9c10c10b3aedf2f7e97015ee23c3f4e639" +checksum = "19b33cdc0483d236cdfff763dae799ccef9646e94fb549a74f7adac6a7f7bb86" dependencies = [ "alloy-genesis", "alloy-primitives", @@ -579,9 +571,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-anvil" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65e3266095e6d8e8028aab5f439c6b8736c5147314f7e606c61597e014cb8a0" +checksum = "83d98fb386a462e143f5efa64350860af39950c49e7c0cbdba419c16793116ef" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -591,9 +583,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-any" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07429a1099cd17227abcddb91b5e38c960aaeb02a6967467f5bb561fbe716ac6" +checksum = "fbde0801a32d21c5f111f037bee7e22874836fba7add34ed4a6919932dd7cf23" dependencies = [ "alloy-consensus-any", "alloy-rpc-types-eth", @@ -602,28 +594,29 @@ dependencies = [ [[package]] name = "alloy-rpc-types-beacon" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59e0e876b20eb9debf316d3e875536f389070635250f22b5a678cf4632a3e0cf" +checksum = "55c8d51ebb7c5fa8be8ea739a3933c5bfea08777d2d662b30b2109ac5ca71e6b" dependencies = [ "alloy-eips", "alloy-primitives", "alloy-rpc-types-engine", + "derive_more", "ethereum_ssz", "ethereum_ssz_derive", "serde", "serde_json", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", "tree_hash", "tree_hash_derive", ] [[package]] name = "alloy-rpc-types-debug" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeff305b7d10cc1c888456d023e7bb8a5ea82e9e42b951e37619b88cc1a1486d" +checksum = "388cf910e66bd4f309a81ef746dcf8f9bca2226e3577890a8d56c5839225cf46" dependencies = [ "alloy-primitives", "derive_more", @@ -633,9 +626,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-engine" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "222ecadcea6aac65e75e32b6735635ee98517aa63b111849ee01ae988a71d685" +checksum = "605ec375d91073851f566a3082548af69a28dca831b27a8be7c1b4c49f5c6ca2" dependencies = [ "alloy-consensus", "alloy-eips", @@ -654,9 +647,9 @@ dependencies = [ [[package]] name = "alloy-rpc-types-eth" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db46b0901ee16bbb68d986003c66dcb74a12f9d9b3c44f8e85d51974f2458f0f" +checksum = "361cd87ead4ba7659bda8127902eda92d17fa7ceb18aba1676f7be10f7222487" dependencies = [ "alloy-consensus", "alloy-consensus-any", @@ -671,14 +664,14 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "alloy-rpc-types-mev" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a60d4baadd3f278faa4e2305cca095dfd4ab286e071b768ff09181d8ae215" +checksum = "1397926d8d06a2531578bafc3e0ec78f97a02f0e6d1631c67d80d22af6a3af02" dependencies = [ "alloy-consensus", "alloy-eips", @@ -691,23 +684,23 @@ dependencies = [ [[package]] name = "alloy-rpc-types-trace" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36f10620724bd45f80c79668a8cdbacb6974f860686998abce28f6196ae79444" +checksum = "de4e95fb0572b97b17751d0fdf5cdc42b0050f9dd9459eddd1bf2e2fbfed0a33" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", "alloy-serde", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "alloy-rpc-types-txpool" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "864f41befa90102d4e02327679699a7e9510930e2924c529e31476086609fa89" +checksum = "cddde1bbd4feeb0d363ae7882af1e2e7955ef77c17f933f31402aad9343b57c5" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -717,9 +710,9 @@ dependencies = [ [[package]] name = "alloy-serde" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5413814be7a22fbc81e0f04a2401fcc3eb25e56fd53b04683e8acecc6e1fe01b" +checksum = "64600fc6c312b7e0ba76f73a381059af044f4f21f43e07f51f1fa76c868fe302" dependencies = [ "alloy-primitives", "arbitrary", @@ -729,9 +722,9 @@ dependencies = [ [[package]] name = "alloy-signer" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53410a18a61916e2c073a6519499514e027b01e77eeaf96acd1df7cf96ef6bb2" +checksum = "5772858492b26f780468ae693405f895d6a27dea6e3eab2c36b6217de47c2647" dependencies = [ "alloy-primitives", "async-trait", @@ -739,14 +732,14 @@ dependencies = [ "either", "elliptic-curve", "k256", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "alloy-signer-local" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6006c4cbfa5d08cadec1fcabea6cb56dc585a30a9fce40bcf81e307d6a71c8e" +checksum = "f4195b803d0a992d8dbaab2ca1986fc86533d4bc80967c0cce7668b26ad99ef9" dependencies = [ "alloy-consensus", "alloy-network", @@ -757,7 +750,7 @@ dependencies = [ "coins-bip39", "k256", "rand 0.8.5", - "thiserror 2.0.16", + "thiserror 2.0.17", "zeroize", ] @@ -772,7 +765,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -784,11 +777,11 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.11.4", + "indexmap 2.12.0", "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "syn-solidity", "tiny-keccak", ] @@ -805,7 +798,7 @@ dependencies = [ "macro-string", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "syn-solidity", ] @@ -833,9 +826,9 @@ dependencies = [ [[package]] name = "alloy-transport" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d94ee404368a3d9910dfe61b203e888c6b0e151a50e147f95da8baff9f9c7763" +checksum = "025a940182bddaeb594c26fe3728525ae262d0806fe6a4befdf5d7bc13d54bce" dependencies = [ "alloy-json-rpc", "alloy-primitives", @@ -847,7 +840,7 @@ dependencies = [ "parking_lot", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tower", "tracing", @@ -857,9 +850,9 @@ dependencies = [ [[package]] name = "alloy-transport-http" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f8a6338d594f6c6481292215ee8f2fd7b986c80aba23f3f44e761a8658de78" +checksum = "e3b5064d1e1e1aabc918b5954e7fb8154c39e77ec6903a581b973198b26628fa" dependencies = [ "alloy-json-rpc", "alloy-transport", @@ -872,9 +865,9 @@ dependencies = [ [[package]] name = "alloy-transport-ipc" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17a37a8ca18006fa0a58c7489645619ff58cfa073f2b29c4e052c9bd114b123a" +checksum = "d47962f3f1d9276646485458dc842b4e35675f42111c9d814ae4711c664c8300" dependencies = [ "alloy-json-rpc", "alloy-pubsub", @@ -892,9 +885,9 @@ dependencies = [ [[package]] name = "alloy-transport-ws" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "679b0122b7bca9d4dc5eb2c0549677a3c53153f6e232f23f4b3ba5575f74ebde" +checksum = "9476a36a34e2fb51b6746d009c53d309a186a825aa95435407f0e07149f4ad2d" dependencies = [ "alloy-pubsub", "alloy-transport", @@ -930,15 +923,15 @@ dependencies = [ [[package]] name = "alloy-tx-macros" -version = "1.0.37" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64c09ec565a90ed8390d82aa08cd3b22e492321b96cb4a3d4f58414683c9e2f" +checksum = "f8e52276fdb553d3c11563afad2898f4085165e4093604afe3d78b69afbf408f" dependencies = [ "alloy-primitives", "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -958,9 +951,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.20" +version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", @@ -973,9 +966,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -1023,7 +1016,7 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1165,7 +1158,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1203,7 +1196,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1292,7 +1285,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1409,7 +1402,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1420,7 +1413,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1458,7 +1451,7 @@ checksum = "ffdcb70bdbc4d478427380519163274ac86e52916e10f0a8889adf0f96d3fee7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1475,29 +1468,14 @@ checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" [[package]] name = "backon" -version = "1.5.2" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "592277618714fbcecda9a02ba7a8781f319d26532a88553bbacc77ba5d2b3a8d" +checksum = "cffb0e931875b666fc4fcb20fee52e9bbd1ef836fd9e9e04ec21555f9f85f7ef" dependencies = [ "fastrand 2.3.0", "tokio", ] -[[package]] -name = "backtrace" -version = "0.3.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" -dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link 0.2.0", -] - [[package]] name = "base-x" version = "0.2.11" @@ -1510,6 +1488,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" +[[package]] +name = "base256emoji" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e9430d9a245a77c92176e649af6e275f20839a48389859d1661e9a128d077c" +dependencies = [ + "const-str", + "match-lookup", +] + [[package]] name = "base64" version = "0.13.1" @@ -1577,9 +1565,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.70.1" +version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ "bitflags 2.9.4", "cexpr", @@ -1588,16 +1576,16 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash", "shlex", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] name = "bindgen" -version = "0.71.1" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags 2.9.4", "cexpr", @@ -1606,9 +1594,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 2.1.1", + "rustc-hash", "shlex", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1671,15 +1659,6 @@ dependencies = [ "wyz", ] -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - [[package]] name = "block-buffer" version = "0.10.4" @@ -1720,9 +1699,9 @@ dependencies = [ "boa_interner", "boa_macros", "boa_string", - "indexmap 2.11.4", + "indexmap 2.12.0", "num-bigint", - "rustc-hash 2.1.1", + "rustc-hash", ] [[package]] @@ -1746,7 +1725,7 @@ dependencies = [ "fast-float2", "hashbrown 0.15.5", "icu_normalizer 1.5.0", - "indexmap 2.11.4", + "indexmap 2.12.0", "intrusive-collections", "itertools 0.13.0", "num-bigint", @@ -1758,7 +1737,7 @@ dependencies = [ "portable-atomic", "rand 0.8.5", "regress", - "rustc-hash 2.1.1", + "rustc-hash", "ryu-js", "serde", "serde_json", @@ -1766,7 +1745,7 @@ dependencies = [ "static_assertions", "tap", "thin-vec", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] @@ -1792,10 +1771,10 @@ dependencies = [ "boa_gc", "boa_macros", "hashbrown 0.15.5", - "indexmap 2.11.4", + "indexmap 2.12.0", "once_cell", - "phf", - "rustc-hash 2.1.1", + "phf 0.11.3", + "rustc-hash", "static_assertions", ] @@ -1807,7 +1786,7 @@ checksum = "9fd3f870829131332587f607a7ff909f1af5fc523fd1b192db55fbbdf52e8d3c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "synstructure", ] @@ -1827,7 +1806,7 @@ dependencies = [ "num-bigint", "num-traits", "regress", - "rustc-hash 2.1.1", + "rustc-hash", ] [[package]] @@ -1844,7 +1823,7 @@ checksum = "7debc13fbf7997bf38bf8e9b20f1ad5e2a7d27a900e1f6039fe244ce30f589b5" dependencies = [ "fast-float2", "paste", - "rustc-hash 2.1.1", + "rustc-hash", "sptr", "static_assertions", ] @@ -1885,7 +1864,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" dependencies = [ - "sha2 0.10.9", + "sha2", "tinyvec", ] @@ -1920,22 +1899,22 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytemuck" -version = "1.23.2" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" +checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.1" +version = "1.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -1955,9 +1934,9 @@ dependencies = [ [[package]] name = "c-kzg" -version = "2.1.4" +version = "2.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "137a2a2878ed823ef1bd73e5441e245602aae5360022113b8ad259ca4b5b8727" +checksum = "e00bf4b112b07b505472dbefd19e37e53307e2bfed5a79e0cc161d58ccd0e687" dependencies = [ "arbitrary", "blst", @@ -1971,9 +1950,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" dependencies = [ "serde_core", ] @@ -2011,7 +1990,7 @@ dependencies = [ "semver 1.0.27", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -2063,9 +2042,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" @@ -2084,7 +2063,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -2137,9 +2116,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.48" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" +checksum = "f4512b90fa68d3a9932cea5184017c5d200f5921df706d45e853537dea51508f" dependencies = [ "clap_builder", "clap_derive", @@ -2147,9 +2126,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.48" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" +checksum = "0025e98baa12e766c67ba13ff4695a887a1eba19569aad00a472546795bd6730" dependencies = [ "anstream", "anstyle", @@ -2159,21 +2138,21 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.47" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] name = "clap_lex" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" [[package]] name = "cmake" @@ -2251,7 +2230,7 @@ dependencies = [ "hmac", "k256", "serde", - "sha2 0.10.9", + "sha2", "thiserror 1.0.69", ] @@ -2267,7 +2246,7 @@ dependencies = [ "once_cell", "pbkdf2", "rand 0.8.5", - "sha2 0.10.9", + "sha2", "thiserror 1.0.69", ] @@ -2285,7 +2264,7 @@ dependencies = [ "generic-array", "ripemd", "serde", - "sha2 0.10.9", + "sha2", "sha3", "thiserror 1.0.69", ] @@ -2393,9 +2372,9 @@ dependencies = [ [[package]] name = "const-hex" -version = "1.16.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6407bff74dea37e0fa3dc1c1c974e5d46405f0c987bf9997a0762adce71eda6" +checksum = "3bb320cac8a0750d7f25280aa97b09c26edfe161164238ecbbb31092b079e735" dependencies = [ "cfg-if", "cpufeatures", @@ -2409,11 +2388,17 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const-str" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f421161cb492475f1661ddc9815a745a1c894592070661180fdec3d4872e9c3" + [[package]] name = "const_format" -version = "0.2.34" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] @@ -2616,21 +2601,21 @@ dependencies = [ [[package]] name = "csv" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" +checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ "csv-core", "itoa", "ryu", - "serde", + "serde_core", ] [[package]] name = "csv-core" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" +checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ "memchr", ] @@ -2668,7 +2653,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2702,7 +2687,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2717,7 +2702,7 @@ dependencies = [ "quote", "serde", "strsim", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2728,7 +2713,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2739,7 +2724,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2792,7 +2777,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d162beedaa69905488a8da94f5ac3edb4dd4788b732fadb7bd120b2625c1976" dependencies = [ "data-encoding", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2851,7 +2836,7 @@ checksum = "ef941ded77d15ca19b40374869ac6000af1c9f2a4c0f3d4c70926287e6364a8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2862,7 +2847,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2883,7 +2868,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2893,7 +2878,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -2914,7 +2899,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "unicode-xid", ] @@ -2939,7 +2924,7 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer 0.10.4", + "block-buffer", "const-oid", "crypto-common", "subtle", @@ -2973,7 +2958,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -3028,7 +3013,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -3093,7 +3078,7 @@ dependencies = [ "ed25519", "rand_core 0.6.4", "serde", - "sha2 0.10.9", + "sha2", "subtle", "zeroize", ] @@ -3107,7 +3092,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -3147,7 +3132,7 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "walkdir", ] @@ -3215,7 +3200,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -3235,7 +3220,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -3251,7 +3236,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -3271,7 +3256,7 @@ checksum = "c853bd72c9e5787f8aafc3df2907c2ed03cff3150c3acd94e2e53a98ab70a8ab" dependencies = [ "cpufeatures", "ring", - "sha2 0.10.9", + "sha2", ] [[package]] @@ -3289,9 +3274,9 @@ dependencies = [ [[package]] name = "ethereum_ssz" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ca8ba45b63c389c6e115b095ca16381534fdcc03cf58176a3f8554db2dbe19b" +checksum = "0dcddb2554d19cde19b099fadddde576929d7a4d0c1cd3512d1fd95cf174375c" dependencies = [ "alloy-primitives", "ethereum_serde_utils", @@ -3304,14 +3289,14 @@ dependencies = [ [[package]] name = "ethereum_ssz_derive" -version = "0.9.0" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0dd55d08012b4e0dfcc92b8d6081234df65f2986ad34cc76eeed69c5e2ce7506" +checksum = "a657b6b3b7e153637dc6bdc6566ad9279d9ee11a15b12cfb24a2e04360637e9f" dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -3335,7 +3320,7 @@ dependencies = [ "reth-ethereum", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -3378,7 +3363,7 @@ dependencies = [ "secp256k1 0.30.0", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -3424,7 +3409,7 @@ dependencies = [ "reth-payload-builder", "reth-tracing", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] @@ -3494,7 +3479,7 @@ dependencies = [ "revm", "revm-primitives", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -3838,9 +3823,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +checksum = "dc5a4e564e38c699f2880d3fda590bedc2e69f3f84cd48b457bd892ce61d0aa9" dependencies = [ "crc32fast", "miniz_oxide", @@ -3959,7 +3944,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -4060,15 +4045,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "js-sys", "libc", "r-efi", - "wasi 0.14.7+wasi-0.2.4", + "wasip2", "wasm-bindgen", ] @@ -4082,12 +4067,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - [[package]] name = "git2" version = "0.20.2" @@ -4186,7 +4165,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.11.4", + "indexmap 2.12.0", "slab", "tokio", "tokio-util", @@ -4195,12 +4174,13 @@ dependencies = [ [[package]] name = "half" -version = "2.6.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "zerocopy", ] [[package]] @@ -4316,7 +4296,7 @@ dependencies = [ "rand 0.9.2", "ring", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tokio", "tracing", @@ -4340,7 +4320,7 @@ dependencies = [ "resolv-conf", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -4496,7 +4476,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.2", + "webpki-roots 1.0.3", ] [[package]] @@ -4517,7 +4497,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2 0.6.1", "tokio", "tower-service", "tracing", @@ -4535,7 +4515,7 @@ dependencies = [ "js-sys", "log", "wasm-bindgen", - "windows-core 0.62.1", + "windows-core 0.62.2", ] [[package]] @@ -4748,7 +4728,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -4805,7 +4785,7 @@ checksum = "a0eb5a3343abf848c0984fe4604b2b105da9539376e24fc0a3b0007411ae4fd9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -4846,9 +4826,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.11.4" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "arbitrary", "equivalent", @@ -4909,7 +4889,7 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -4945,17 +4925,6 @@ dependencies = [ "memoffset", ] -[[package]] -name = "io-uring" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" -dependencies = [ - "bitflags 2.9.4", - "cfg-if", - "libc", -] - [[package]] name = "ipconfig" version = "0.3.2" @@ -5062,7 +5031,7 @@ version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "libc", ] @@ -5111,7 +5080,7 @@ dependencies = [ "rustls-pki-types", "rustls-platform-verifier", "soketto", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-rustls", "tokio-util", @@ -5136,10 +5105,10 @@ dependencies = [ "parking_lot", "pin-project", "rand 0.9.2", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tower", @@ -5164,7 +5133,7 @@ dependencies = [ "rustls-platform-verifier", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tower", "url", @@ -5180,7 +5149,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -5202,7 +5171,7 @@ dependencies = [ "serde", "serde_json", "soketto", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -5219,7 +5188,7 @@ dependencies = [ "http", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -5274,7 +5243,7 @@ dependencies = [ "elliptic-curve", "once_cell", "serdect", - "sha2 0.10.9", + "sha2", "signature", ] @@ -5325,9 +5294,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.176" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libgit2-sys" @@ -5348,7 +5317,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -5370,19 +5339,19 @@ dependencies = [ "k256", "multihash", "quick-protobuf", - "sha2 0.10.9", - "thiserror 2.0.16", + "sha2", + "thiserror 2.0.17", "tracing", "zeroize", ] [[package]] name = "libproc" -version = "0.14.10" +version = "0.14.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78a09b56be5adbcad5aa1197371688dc6bb249a26da3bca2011ee2fb987ebfb" +checksum = "a54ad7278b8bc5301d5ffd2a94251c004feb971feba96c971ea4063645990757" dependencies = [ - "bindgen 0.70.1", + "bindgen 0.72.1", "errno", "libc", ] @@ -5398,52 +5367,6 @@ dependencies = [ "redox_syscall", ] -[[package]] -name = "libsecp256k1" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79019718125edc905a079a70cfa5f3820bc76139fc91d6f9abc27ea2a887139" -dependencies = [ - "arrayref", - "base64 0.22.1", - "digest 0.9.0", - "libsecp256k1-core", - "libsecp256k1-gen-ecmult", - "libsecp256k1-gen-genmult", - "rand 0.8.5", - "serde", - "sha2 0.9.9", -] - -[[package]] -name = "libsecp256k1-core" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be9b9bb642d8522a44d533eab56c16c738301965504753b03ad1de3425d5451" -dependencies = [ - "crunchy", - "digest 0.9.0", - "subtle", -] - -[[package]] -name = "libsecp256k1-gen-ecmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3038c808c55c87e8a172643a7d87187fc6c4174468159cb3090659d55bcb4809" -dependencies = [ - "libsecp256k1-core", -] - -[[package]] -name = "libsecp256k1-gen-genmult" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8d6ba2cec9eacc40e6e8ccc98931840301f1006e95647ceb2dd5c3aa06f7c" -dependencies = [ - "libsecp256k1-core", -] - [[package]] name = "libz-sys" version = "1.1.22" @@ -5504,11 +5427,10 @@ checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "lock_api" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ - "autocfg", "scopeguard", "serde", ] @@ -5583,9 +5505,9 @@ checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a" [[package]] name = "mach2" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +checksum = "6a1b95cd5421ec55b445b5ae102f5ea0e768de1f82bd3001e11f426c269c3aea" dependencies = [ "libc", ] @@ -5598,7 +5520,18 @@ checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", +] + +[[package]] +name = "match-lookup" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1265724d8cb29dbbc2b0f06fffb8bf1a8c0cf73a78eede9ba73a4a66c52a981e" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", ] [[package]] @@ -5653,7 +5586,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -5663,7 +5596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd7399781913e5393588a8d8c6a2867bf85fb38eaf2502fdce465aad2dc6f034" dependencies = [ "base64 0.22.1", - "indexmap 2.11.4", + "indexmap 2.12.0", "metrics", "metrics-util", "quanta", @@ -5672,18 +5605,18 @@ dependencies = [ [[package]] name = "metrics-process" -version = "2.4.0" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a82c8add4382f29a122fa64fff1891453ed0f6b2867d971e7d60cb8dfa322ff" +checksum = "f615e08e049bd14a44c4425415782efb9bcd479fc1e19ddeb971509074c060d0" dependencies = [ "libc", "libproc", "mach2", "metrics", "once_cell", - "procfs", + "procfs 0.18.0", "rlimit", - "windows 0.58.0", + "windows 0.62.2", ] [[package]] @@ -5695,7 +5628,7 @@ dependencies = [ "crossbeam-epoch", "crossbeam-utils", "hashbrown 0.15.5", - "indexmap 2.11.4", + "indexmap 2.12.0", "metrics", "ordered-float", "quanta", @@ -5719,7 +5652,7 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -5769,6 +5702,7 @@ checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "serde", + "simd-adler32", ] [[package]] @@ -5849,11 +5783,12 @@ dependencies = [ [[package]] name = "multibase" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3539ec3c1f04ac9748a260728e855f261b4977f5c3406612c884564f329404" +checksum = "8694bb4835f452b0e3bb06dbebb1d6fc5385b6ca1caf2e55fd165c042390ec77" dependencies = [ "base-x", + "base256emoji", "data-encoding", "data-encoding-macro", ] @@ -5913,11 +5848,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.50.1" +version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -6030,7 +5965,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -6044,9 +5979,9 @@ dependencies = [ [[package]] name = "nybbles" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa11e84403164a9f12982ab728f3c67c6fd4ab5b5f0254ffc217bdbd3b28ab0" +checksum = "2c4b5ecbd0beec843101bffe848217f770e8b8da81d8355b7d6e226f2199b3dc" dependencies = [ "alloy-rlp", "arbitrary", @@ -6057,15 +5992,6 @@ dependencies = [ "smallvec", ] -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -6090,9 +6016,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "op-alloy-consensus" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a501241474c3118833d6195312ae7eb7cc90bbb0d5f524cbb0b06619e49ff67" +checksum = "cf1fc8aa0e2f5b136d101630be009e4e6dbdd1f17bc3ce670f431511600d2930" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6105,7 +6031,7 @@ dependencies = [ "derive_more", "serde", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -6116,9 +6042,9 @@ checksum = "a79f352fc3893dcd670172e615afef993a41798a1d3fc0db88a3e60ef2e70ecc" [[package]] name = "op-alloy-network" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f80108e3b36901200a4c5df1db1ee9ef6ce685b59ea79d7be1713c845e3765da" +checksum = "7c5cca341184dbfcb49dbc124e5958e6a857499f04782907e5d969abb644e0b6" dependencies = [ "alloy-consensus", "alloy-network", @@ -6132,9 +6058,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-jsonrpsee" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8eb878fc5ea95adb5abe55fb97475b3eb0dcc77dfcd6f61bd626a68ae0bdba1" +checksum = "190e9884a69012d4abc26d1c0bc60fe01d57899ab5417c8f38105ffaaab4149b" dependencies = [ "alloy-primitives", "jsonrpsee", @@ -6142,9 +6068,9 @@ dependencies = [ [[package]] name = "op-alloy-rpc-types" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "753d6f6b03beca1ba9cbd344c05fee075a2ce715ee9d61981c10b9c764a824a2" +checksum = "274972c3c5e911b6675f6794ea0476b05e0bc1ea7e464f99ec2dc01b76d2eeb6" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6157,14 +6083,14 @@ dependencies = [ "op-alloy-consensus", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "op-alloy-rpc-types-engine" -version = "0.20.0" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14e50c94013a1d036a529df259151991dbbd6cf8dc215e3b68b784f95eec60e6" +checksum = "860edb8d5a8d54bbcdabcbd8642c45b974351ce4e10ed528dd4508eee2a43833" dependencies = [ "alloy-consensus", "alloy-eips", @@ -6179,7 +6105,7 @@ dependencies = [ "op-alloy-consensus", "serde", "snap", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -6206,9 +6132,9 @@ dependencies = [ [[package]] name = "op-revm" -version = "10.1.0" +version = "11.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9ba4f4693811e73449193c8bd656d3978f265871916882e6a51a487e4f96217" +checksum = "b1d721c4c196273dd135ea5b823cd573ea8735cd3c5f2c19fcb91ee3af655351" dependencies = [ "auto_impl", "revm", @@ -6237,7 +6163,7 @@ dependencies = [ "futures-sink", "js-sys", "pin-project-lite", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -6267,7 +6193,7 @@ dependencies = [ "opentelemetry_sdk", "prost", "reqwest", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -6302,7 +6228,7 @@ dependencies = [ "opentelemetry", "percent-encoding", "rand 0.9.2", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -6329,7 +6255,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2 0.10.9", + "sha2", ] [[package]] @@ -6369,7 +6295,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -6380,9 +6306,9 @@ checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", @@ -6390,15 +6316,15 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.11" +version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall", "smallvec", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -6419,12 +6345,12 @@ dependencies = [ [[package]] name = "pem" -version = "3.0.5" +version = "3.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" dependencies = [ "base64 0.22.1", - "serde", + "serde_core", ] [[package]] @@ -6435,12 +6361,11 @@ checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pest" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e0a3a33733faeaf8651dfee72dd0f388f0c8e5ad496a3478fa5a922f49cfa8" +checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" dependencies = [ "memchr", - "thiserror 2.0.16", "ucd-trie", ] @@ -6460,8 +6385,18 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_macros", - "phf_shared", + "phf_macros 0.11.3", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros 0.13.1", + "phf_shared 0.13.1", "serde", ] @@ -6471,21 +6406,44 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared", + "phf_shared 0.11.3", "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand 2.3.0", + "phf_shared 0.13.1", +] + [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator 0.13.1", + "phf_shared 0.13.1", + "proc-macro2", + "quote", + "syn 2.0.107", ] [[package]] @@ -6497,6 +6455,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -6514,7 +6481,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -6647,7 +6614,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -6676,7 +6643,7 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ - "toml_edit 0.23.6", + "toml_edit 0.23.7", ] [[package]] @@ -6698,7 +6665,7 @@ dependencies = [ "proc-macro-error-attr2", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -6720,10 +6687,21 @@ dependencies = [ "chrono", "flate2", "hex", - "procfs-core", + "procfs-core 0.17.0", "rustix 0.38.44", ] +[[package]] +name = "procfs" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25485360a54d6861439d60facef26de713b1e126bf015ec8f98239467a2b82f7" +dependencies = [ + "bitflags 2.9.4", + "procfs-core 0.18.0", + "rustix 1.1.2", +] + [[package]] name = "procfs-core" version = "0.17.0" @@ -6735,6 +6713,16 @@ dependencies = [ "hex", ] +[[package]] +name = "procfs-core" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6401bf7b6af22f78b563665d15a22e9aef27775b79b149a66ca022468a4e405" +dependencies = [ + "bitflags 2.9.4", + "hex", +] + [[package]] name = "proptest" version = "1.8.0" @@ -6773,7 +6761,7 @@ checksum = "4ee1c9ac207483d5e7db4940700de86a9aae46ef90c48b57f99fe7edb8345e49" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -6784,7 +6772,7 @@ checksum = "095a99f75c69734802359b682be8daaf8980296731f6470434ea2c652af1dd30" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -6807,7 +6795,7 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -6862,10 +6850,10 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", - "socket2 0.6.0", - "thiserror 2.0.16", + "socket2 0.6.1", + "thiserror 2.0.17", "tokio", "tracing", "web-time", @@ -6878,15 +6866,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", - "getrandom 0.3.3", + "getrandom 0.3.4", "lru-slab", "rand 0.9.2", "ring", - "rustc-hash 2.1.1", + "rustc-hash", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.16", + "thiserror 2.0.17", "tinyvec", "tracing", "web-time", @@ -6901,16 +6889,16 @@ dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.6.0", + "socket2 0.6.1", "tracing", "windows-sys 0.60.2", ] [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -7017,7 +7005,7 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "serde", ] @@ -7106,9 +7094,9 @@ checksum = "d3edd4d5d42c92f0a659926464d4cce56b562761267ecf0f469d85b7de384175" [[package]] name = "redox_syscall" -version = "0.5.17" +version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags 2.9.4", ] @@ -7132,34 +7120,34 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "ref-cast" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a0ae411dbe946a674d89546582cea4ba2bb8defac896622d6496f14c23ba5cf" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] name = "regex" -version = "1.11.3" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -7169,9 +7157,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -7180,9 +7168,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "regress" @@ -7202,9 +7190,9 @@ checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" [[package]] name = "reqwest" -version = "0.12.23" +version = "0.12.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" +checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64 0.22.1", "bytes", @@ -7240,7 +7228,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", - "webpki-roots 1.0.2", + "webpki-roots 1.0.3", ] [[package]] @@ -7352,7 +7340,7 @@ dependencies = [ "reth-tracing", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tower", "tracing", @@ -7497,6 +7485,7 @@ dependencies = [ "serde", "serde_json", "tar", + "tempfile", "tokio", "tokio-stream", "toml", @@ -7528,7 +7517,7 @@ dependencies = [ "secp256k1 0.30.0", "serde", "snmalloc-rs", - "thiserror 2.0.16", + "thiserror 2.0.17", "tikv-jemallocator", "tracy-client", ] @@ -7564,7 +7553,7 @@ dependencies = [ "proc-macro2", "quote", "similar-asserts", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -7593,7 +7582,7 @@ dependencies = [ "auto_impl", "reth-execution-types", "reth-primitives-traits", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -7660,13 +7649,13 @@ dependencies = [ "reth-static-file-types", "reth-storage-errors", "reth-tracing", - "rustc-hash 2.1.1", + "rustc-hash", "serde", "serde_json", "strum 0.27.2", "sysinfo", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -7725,7 +7714,7 @@ dependencies = [ "reth-trie-db", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -7766,7 +7755,7 @@ dependencies = [ "schnellru", "secp256k1 0.30.0", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -7792,7 +7781,7 @@ dependencies = [ "reth-network-peers", "reth-tracing", "secp256k1 0.30.0", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -7819,7 +7808,7 @@ dependencies = [ "secp256k1 0.30.0", "serde", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -7856,7 +7845,7 @@ dependencies = [ "reth-testing-utils", "reth-tracing", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -7940,9 +7929,9 @@ dependencies = [ "rand 0.8.5", "reth-network-peers", "secp256k1 0.30.0", - "sha2 0.10.9", + "sha2", "sha3", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -7993,7 +7982,7 @@ dependencies = [ "reth-primitives-traits", "reth-trie-common", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] @@ -8093,7 +8082,7 @@ dependencies = [ "schnellru", "serde_json", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -8143,7 +8132,7 @@ dependencies = [ "snap", "tempfile", "test-case", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] @@ -8158,7 +8147,7 @@ dependencies = [ "futures-util", "reqwest", "reth-fs-util", - "sha2 0.10.9", + "sha2", "tempfile", "test-case", "tokio", @@ -8197,7 +8186,7 @@ dependencies = [ "reth-consensus", "reth-execution-errors", "reth-storage-errors", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -8231,7 +8220,7 @@ dependencies = [ "serde", "snap", "test-fuzz", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -8260,7 +8249,7 @@ dependencies = [ "reth-ethereum-primitives", "reth-primitives-traits", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -8355,8 +8344,8 @@ dependencies = [ "reth-primitives-traits", "serde", "serde_json", - "sha2 0.10.9", - "thiserror 2.0.16", + "sha2", + "thiserror 2.0.17", ] [[package]] @@ -8369,7 +8358,7 @@ dependencies = [ "arbitrary", "auto_impl", "once_cell", - "rustc-hash 2.1.1", + "rustc-hash", ] [[package]] @@ -8494,7 +8483,7 @@ dependencies = [ "alloy-rlp", "nybbles", "reth-storage-errors", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -8555,7 +8544,7 @@ dependencies = [ "rmp-serde", "secp256k1 0.30.0", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-util", "tracing", @@ -8588,7 +8577,7 @@ dependencies = [ "reth-tasks", "reth-transaction-pool", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] @@ -8615,7 +8604,7 @@ version = "1.8.2" dependencies = [ "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -8665,7 +8654,7 @@ dependencies = [ "reth-tracing", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -8687,7 +8676,7 @@ dependencies = [ "reth-mdbx-sys", "smallvec", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -8726,7 +8715,7 @@ dependencies = [ "reqwest", "reth-tracing", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -8778,12 +8767,12 @@ dependencies = [ "reth-tokio-util", "reth-tracing", "reth-transaction-pool", - "rustc-hash 2.1.1", + "rustc-hash", "schnellru", "secp256k1 0.30.0", "serde", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-util", @@ -8810,7 +8799,7 @@ dependencies = [ "reth-network-types", "reth-tokio-util", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", ] @@ -8849,7 +8838,7 @@ dependencies = [ "secp256k1 0.30.0", "serde_json", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "url", ] @@ -8880,7 +8869,7 @@ dependencies = [ "reth-fs-util", "serde", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "zstd", ] @@ -9023,7 +9012,7 @@ dependencies = [ "serde", "shellexpand", "strum 0.27.2", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "toml", "tracing", @@ -9100,7 +9089,7 @@ dependencies = [ "reth-transaction-pool", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tokio-tungstenite", @@ -9142,7 +9131,7 @@ dependencies = [ "metrics-exporter-prometheus", "metrics-process", "metrics-util", - "procfs", + "procfs 0.17.0", "reqwest", "reth-metrics", "reth-tasks", @@ -9228,7 +9217,7 @@ dependencies = [ "serde", "serde_json", "tar-no-std", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -9307,7 +9296,7 @@ dependencies = [ "reth-trie", "reth-trie-common", "revm", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", ] @@ -9337,7 +9326,7 @@ dependencies = [ "reth-rpc-eth-api", "reth-storage-errors", "revm", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -9358,7 +9347,7 @@ dependencies = [ "reth-optimism-trie", "reth-provider", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] @@ -9502,8 +9491,8 @@ dependencies = [ "reth-transaction-pool", "revm", "serde", - "sha2 0.10.9", - "thiserror 2.0.16", + "sha2", + "thiserror 2.0.17", "tracing", ] @@ -9589,7 +9578,7 @@ dependencies = [ "reth-transaction-pool", "revm", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tower", @@ -9634,7 +9623,7 @@ dependencies = [ "strum 0.27.2", "tempfile", "test-case", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -9671,7 +9660,7 @@ dependencies = [ "reth-storage-api", "reth-transaction-pool", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -9723,7 +9712,7 @@ dependencies = [ "reth-errors", "reth-primitives-traits", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", ] @@ -9801,7 +9790,7 @@ dependencies = [ "serde", "serde_json", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -9878,8 +9867,8 @@ dependencies = [ "reth-testing-utils", "reth-tokio-util", "reth-tracing", - "rustc-hash 2.1.1", - "thiserror 2.0.16", + "rustc-hash", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -9898,7 +9887,7 @@ dependencies = [ "reth-codecs", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "toml", ] @@ -10042,8 +10031,8 @@ dependencies = [ "revm-primitives", "serde", "serde_json", - "sha2 0.10.9", - "thiserror 2.0.16", + "sha2", + "thiserror 2.0.17", "tokio", "tokio-stream", "tower", @@ -10144,7 +10133,7 @@ dependencies = [ "reth-transaction-pool", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-util", "tower", @@ -10176,7 +10165,7 @@ dependencies = [ "reth-storage-api", "revm-context", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -10230,7 +10219,7 @@ dependencies = [ "reth-testing-utils", "reth-transaction-pool", "serde", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -10319,7 +10308,7 @@ dependencies = [ "schnellru", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -10409,7 +10398,7 @@ dependencies = [ "reth-trie", "reth-trie-db", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -10437,7 +10426,7 @@ dependencies = [ "reth-static-file-types", "reth-testing-utils", "reth-tokio-util", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -10469,6 +10458,7 @@ dependencies = [ "alloy-rpc-types-debug", "alloy-trie", "itertools 0.14.0", + "k256", "reth-chainspec", "reth-consensus", "reth-errors", @@ -10479,9 +10469,10 @@ dependencies = [ "reth-revm", "reth-trie-common", "reth-trie-sparse", + "secp256k1 0.30.0", "serde", "serde_with", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -10553,7 +10544,7 @@ dependencies = [ "reth-prune-types", "reth-static-file-types", "revm-database-interface", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] @@ -10596,7 +10587,7 @@ dependencies = [ "pin-project", "rayon", "reth-metrics", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", "tracing-futures", @@ -10691,15 +10682,15 @@ dependencies = [ "reth-storage-api", "reth-tasks", "reth-tracing", - "revm-interpreter", + "revm-interpreter 27.0.2", "revm-primitives", - "rustc-hash 2.1.1", + "rustc-hash", "schnellru", "serde", "serde_json", "smallvec", "tempfile", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tokio-stream", "tracing", @@ -10821,7 +10812,7 @@ dependencies = [ "reth-trie-common", "reth-trie-db", "reth-trie-sparse", - "thiserror 2.0.16", + "thiserror 2.0.17", "tokio", "tracing", ] @@ -10897,9 +10888,9 @@ dependencies = [ [[package]] name = "revm" -version = "29.0.1" +version = "30.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718d90dce5f07e115d0e66450b1b8aa29694c1cf3f89ebddaddccc2ccbd2f13e" +checksum = "76df793c6ef3bef8f88f05b3873ebebce1494385a3ce8f58ad2e2e111aa0de11" dependencies = [ "revm-bytecode", "revm-context", @@ -10908,7 +10899,7 @@ dependencies = [ "revm-database-interface", "revm-handler", "revm-inspector", - "revm-interpreter", + "revm-interpreter 28.0.0", "revm-precompile", "revm-primitives", "revm-state", @@ -10916,21 +10907,21 @@ dependencies = [ [[package]] name = "revm-bytecode" -version = "6.2.2" +version = "7.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c52031b73cae95d84cd1b07725808b5fd1500da3e5e24574a3b2dc13d9f16d" +checksum = "451748b17ac78bd2b0748ec472a5392cd78fc0f7d19d528be44770fda28fd6f7" dependencies = [ "bitvec", - "phf", + "phf 0.13.1", "revm-primitives", "serde", ] [[package]] name = "revm-context" -version = "9.1.0" +version = "10.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a20c98e7008591a6f012550c2a00aa36cba8c14cc88eb88dec32eb9102554b4" +checksum = "7adcce0c14cf59b7128de34185a0fbf8f63309539b9263b35ead870d73584114" dependencies = [ "bitvec", "cfg-if", @@ -10945,9 +10936,9 @@ dependencies = [ [[package]] name = "revm-context-interface" -version = "10.2.0" +version = "11.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50d241ed1ce647b94caf174fcd0239b7651318b2c4c06b825b59b973dfb8495" +checksum = "7d620a9725e443c171fb195a074331fa4a745fa5cbb0018b4bbf42619e64b563" dependencies = [ "alloy-eip2930", "alloy-eip7702", @@ -10961,9 +10952,9 @@ dependencies = [ [[package]] name = "revm-database" -version = "7.0.5" +version = "9.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a276ed142b4718dcf64bc9624f474373ed82ef20611025045c3fb23edbef9c" +checksum = "fdefd7f40835e992bab40a245124cb1243e6c7a1c4659798827c809a59b0fea9" dependencies = [ "alloy-eips", "revm-bytecode", @@ -10975,9 +10966,9 @@ dependencies = [ [[package]] name = "revm-database-interface" -version = "7.0.5" +version = "8.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c523c77e74eeedbac5d6f7c092e3851dbe9c7fec6f418b85992bd79229db361" +checksum = "aa488a73ac2738f11478650cdf1a0f263864c09d5f0e9bf6309e891a05323c60" dependencies = [ "auto_impl", "either", @@ -10988,9 +10979,9 @@ dependencies = [ [[package]] name = "revm-handler" -version = "10.0.1" +version = "11.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "550331ea85c1d257686e672081576172fe3d5a10526248b663bbf54f1bef226a" +checksum = "b1d8049b2fbff6636150f4740c95369aa174e41b0383034e0e256cfdffcfcd23" dependencies = [ "auto_impl", "derive-where", @@ -10998,7 +10989,7 @@ dependencies = [ "revm-context", "revm-context-interface", "revm-database-interface", - "revm-interpreter", + "revm-interpreter 28.0.0", "revm-precompile", "revm-primitives", "revm-state", @@ -11007,16 +10998,16 @@ dependencies = [ [[package]] name = "revm-inspector" -version = "10.0.1" +version = "11.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c0a6e9ccc2ae006f5bed8bd80cd6f8d3832cd55c5e861b9402fdd556098512f" +checksum = "e2a21dd773b654ec7e080025eecef4ac84c711150d1bd36acadf0546f471329a" dependencies = [ "auto_impl", "either", "revm-context", "revm-database-interface", "revm-handler", - "revm-interpreter", + "revm-interpreter 28.0.0", "revm-primitives", "revm-state", "serde", @@ -11025,9 +11016,9 @@ dependencies = [ [[package]] name = "revm-inspectors" -version = "0.30.0" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9b329afcc0f9fd5adfa2c6349a7435a8558e82bcae203142103a9a95e2a63b6" +checksum = "782c38fa94f99b4b15f1690bffc2c3cbf06a0f460cf163b470d126914b47d343" dependencies = [ "alloy-primitives", "alloy-rpc-types-eth", @@ -11040,26 +11031,40 @@ dependencies = [ "revm", "serde", "serde_json", - "thiserror 2.0.16", + "thiserror 2.0.17", ] [[package]] name = "revm-interpreter" -version = "25.0.3" +version = "27.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06575dc51b1d8f5091daa12a435733a90b4a132dca7ccee0666c7db3851bc30c" +checksum = "0834fc25c020061f0f801d8de8bb53c88a63631cca5884a6c65b90c85e241138" dependencies = [ "revm-bytecode", "revm-context-interface", "revm-primitives", + "revm-state", + "serde", +] + +[[package]] +name = "revm-interpreter" +version = "28.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1de5c790122f8ded67992312af8acd41ccfcee629b25b819e10c5b1f69caf57" +dependencies = [ + "revm-bytecode", + "revm-context-interface", + "revm-primitives", + "revm-state", "serde", ] [[package]] name = "revm-precompile" -version = "27.0.0" +version = "28.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b57d4bd9e6b5fe469da5452a8a137bc2d030a3cd47c46908efc615bbc699da" +checksum = "e57aadd7a2087705f653b5aaacc8ad4f8e851f5d330661e3f4c43b5475bbceae" dependencies = [ "ark-bls12-381", "ark-bn254", @@ -11072,20 +11077,19 @@ dependencies = [ "c-kzg", "cfg-if", "k256", - "libsecp256k1", "p256", "revm-primitives", "ripemd", "rug", "secp256k1 0.31.1", - "sha2 0.10.9", + "sha2", ] [[package]] name = "revm-primitives" -version = "20.2.1" +version = "21.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa29d9da06fe03b249b6419b33968ecdf92ad6428e2f012dc57bcd619b5d94e" +checksum = "536f30e24c3c2bf0d3d7d20fa9cf99b93040ed0f021fd9301c78cddb0dacda13" dependencies = [ "alloy-primitives", "num_enum", @@ -11095,9 +11099,9 @@ dependencies = [ [[package]] name = "revm-state" -version = "7.0.5" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f64fbacb86008394aaebd3454f9643b7d5a782bd251135e17c5b33da592d84d" +checksum = "9e6bd5e669b02007872a8ca2643a14e308fe1739ee4475d74122587c3388a06a" dependencies = [ "bitflags 2.9.4", "revm-bytecode", @@ -11236,7 +11240,7 @@ dependencies = [ "regex", "relative-path", "rustc_version 0.4.1", - "syn 2.0.106", + "syn 2.0.107", "unicode-ident", ] @@ -11293,12 +11297,6 @@ version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - [[package]] name = "rustc-hash" version = "2.1.1" @@ -11355,14 +11353,14 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.11.0", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] name = "rustls" -version = "0.23.32" +version = "0.23.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c25631629d034ce7cd9940adc9d45762d46de2b0f57193c4443b92c6d4d40" +checksum = "751e04a496ca00bb97a5e043158d23d66b5aabf2e1d5aa2a0aaebb1aafe6f82c" dependencies = [ "log", "once_cell", @@ -11375,9 +11373,9 @@ dependencies = [ [[package]] name = "rustls-native-certs" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +checksum = "9980d917ebb0c0536119ba501e90834767bffc3d60641457fd84a1f3fd337923" dependencies = [ "openssl-probe", "rustls-pki-types", @@ -11424,9 +11422,9 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-webpki" -version = "0.103.6" +version = "0.103.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" +checksum = "e10b3f4191e8a80e6b43eebabfac91e5dcecebb27a71f04e820c47ec41d314bf" dependencies = [ "ring", "rustls-pki-types", @@ -11441,9 +11439,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rusty-fork" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" dependencies = [ "fnv", "quick-error", @@ -11478,7 +11476,7 @@ version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -11586,9 +11584,9 @@ dependencies = [ [[package]] name = "security-framework" -version = "3.5.0" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc198e42d9b7510827939c9a15f5062a0c913f3371d765977e586d2fe6c16f4a" +checksum = "b3297343eaf830f66ede390ea39da1d462b6b0c1b000f420d0a83f898bbbe6ef" dependencies = [ "bitflags 2.9.4", "core-foundation", @@ -11683,7 +11681,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -11692,7 +11690,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.0", "itoa", "memchr", "ryu", @@ -11734,19 +11732,18 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.14.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c522100790450cf78eeac1507263d0a350d4d5b30df0c8e1fe051a10c22b376e" +checksum = "6093cd8c01b25262b84927e0f7151692158fab02d961e04c979d3903eba7ecc5" dependencies = [ "base64 0.22.1", "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.11.4", + "indexmap 2.12.0", "schemars 0.9.0", "schemars 1.0.4", - "serde", - "serde_derive", + "serde_core", "serde_json", "serde_with_macros", "time", @@ -11754,14 +11751,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.14.1" +version = "3.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327ada00f7d64abaac1e55a6911e90cf665aa051b9a561c7006c157f4633135e" +checksum = "a7e6c180db0816026a61afa1cff5344fb7ebded7e4d3062772179f2501481c27" dependencies = [ "darling 0.21.3", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -11785,19 +11782,6 @@ dependencies = [ "digest 0.10.7", ] -[[package]] -name = "sha2" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" -dependencies = [ - "block-buffer 0.9.0", - "cfg-if", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", -] - [[package]] name = "sha2" version = "0.10.9" @@ -11893,6 +11877,12 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "similar" version = "2.7.0" @@ -11922,7 +11912,7 @@ checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" dependencies = [ "num-bigint", "num-traits", - "thiserror 2.0.16", + "thiserror 2.0.17", "time", ] @@ -12006,12 +11996,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -12048,9 +12038,9 @@ checksum = "3b9b39299b249ad65f3b7e96443bad61c02ca5cd3589f46cb6d610a0fd6c0d6a" [[package]] name = "stable_deref_trait" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "static_assertions" @@ -12092,7 +12082,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12104,7 +12094,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12126,9 +12116,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.106" +version = "2.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" +checksum = "2a26dbd934e5451d21ef060c018dae56fc073894c5a7896f882928a76e6d081b" dependencies = [ "proc-macro2", "quote", @@ -12144,7 +12134,7 @@ dependencies = [ "paste", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12164,7 +12154,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12221,10 +12211,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" dependencies = [ "fastrand 2.3.0", - "getrandom 0.3.3", + "getrandom 0.3.4", "once_cell", "rustix 1.1.2", - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -12245,7 +12235,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12256,7 +12246,7 @@ checksum = "5c89e72a01ed4c579669add59014b9a524d609c0c88c6a585ce37485879f6ffb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "test-case-core", ] @@ -12296,7 +12286,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12329,11 +12319,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.16", + "thiserror-impl 2.0.17", ] [[package]] @@ -12344,18 +12334,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] name = "thiserror-impl" -version = "2.0.16" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12378,9 +12368,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-ctl" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21f216790c8df74ce3ab25b534e0718da5a1916719771d3fec23315c99e468b" +checksum = "661f1f6a57b3a36dc9174a2c10f19513b4866816e13425d3e418b11cc37bc24c" dependencies = [ "libc", "paste", @@ -12389,9 +12379,9 @@ dependencies = [ [[package]] name = "tikv-jemalloc-sys" -version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" +version = "0.6.1+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d" +checksum = "cd8aa5b2ab86a2cefa406d889139c162cbb230092f7d1d7cbc1716405d852a3b" dependencies = [ "cc", "libc", @@ -12399,9 +12389,9 @@ dependencies = [ [[package]] name = "tikv-jemallocator" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cec5ff18518d81584f477e9bfdf957f5bb0979b0bac3af4ca30b5b3ae2d2865" +checksum = "0359b4327f954e0567e69fb191cf1436617748813819c94b8cd4a431422d053a" dependencies = [ "libc", "tikv-jemalloc-sys", @@ -12497,33 +12487,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.47.1" +version = "1.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" dependencies = [ - "backtrace", "bytes", - "io-uring", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", - "slab", - "socket2 0.6.0", + "socket2 0.6.1", "tokio-macros", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12603,9 +12590,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" dependencies = [ "serde_core", ] @@ -12616,7 +12603,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.11.4", + "indexmap 2.12.0", "serde", "serde_spanned", "toml_datetime 0.6.11", @@ -12626,21 +12613,21 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.23.6" +version = "0.23.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" +checksum = "6485ef6d0d9b5d0ec17244ff7eb05310113c3f316f2d14200d4de56b3cb98f8d" dependencies = [ - "indexmap 2.11.4", - "toml_datetime 0.7.2", + "indexmap 2.12.0", + "toml_datetime 0.7.3", "toml_parser", "winnow", ] [[package]] name = "toml_parser" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" dependencies = [ "winnow", ] @@ -12692,7 +12679,7 @@ dependencies = [ "futures-core", "futures-util", "hdrhistogram", - "indexmap 2.11.4", + "indexmap 2.12.0", "pin-project-lite", "slab", "sync_wrapper", @@ -12778,7 +12765,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12846,7 +12833,7 @@ dependencies = [ "opentelemetry_sdk", "rustversion", "smallvec", - "thiserror 2.0.16", + "thiserror 2.0.17", "tracing", "tracing-core", "tracing-log", @@ -12938,7 +12925,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -12953,9 +12940,9 @@ dependencies = [ [[package]] name = "triomphe" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef8f7726da4807b58ea5c96fdc122f80702030edc33b35aff9190a51148ccc85" +checksum = "dd69c5aa8f924c7519d6372789a74eac5b94fb0f8fcf0d4a97eb0bfc3e785f39" [[package]] name = "try-lock" @@ -12978,15 +12965,15 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1", - "thiserror 2.0.16", + "thiserror 2.0.17", "utf-8", ] [[package]] name = "typenum" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "ucd-trie" @@ -13141,7 +13128,7 @@ version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ - "getrandom 0.3.3", + "getrandom 0.3.4", "js-sys", "wasm-bindgen", ] @@ -13219,7 +13206,7 @@ checksum = "d674d135b4a8c1d7e813e2f8d1c9a58308aee4a680323066025e53132218bd91" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -13268,15 +13255,6 @@ version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" -[[package]] -name = "wasi" -version = "0.14.7+wasi-0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" -dependencies = [ - "wasip2", -] - [[package]] name = "wasip2" version = "1.0.1+wasi-0.2.4" @@ -13309,7 +13287,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "wasm-bindgen-shared", ] @@ -13344,7 +13322,7 @@ checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -13411,14 +13389,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75c7f0ef91146ebfb530314f5f1d24528d7f0767efbfd31dce919275413e393e" dependencies = [ - "webpki-root-certs 1.0.2", + "webpki-root-certs 1.0.3", ] [[package]] name = "webpki-root-certs" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4ffd8df1c57e87c325000a3d6ef93db75279dc3a231125aac571650f22b12a" +checksum = "05d651ec480de84b762e7be71e6efa7461699c19d9e2c272c8d93455f567786e" dependencies = [ "rustls-pki-types", ] @@ -13429,23 +13407,23 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.2", + "webpki-roots 1.0.3", ] [[package]] name = "webpki-roots" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" +checksum = "32b130c0d2d49f8b6889abc456e795e82525204f27c42cf767cf0d7734e089b8" dependencies = [ "rustls-pki-types", ] [[package]] name = "widestring" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd7cf3379ca1aac9eea11fba24fd7e315d621f8dfe35c8d7d2be8b793726e07d" +checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" [[package]] name = "winapi" @@ -13469,7 +13447,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.61.1", + "windows-sys 0.61.2", ] [[package]] @@ -13490,25 +13468,27 @@ dependencies = [ [[package]] name = "windows" -version = "0.58.0" +version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", + "windows-collections 0.2.0", + "windows-core 0.61.2", + "windows-future 0.2.1", + "windows-link 0.1.3", + "windows-numerics 0.2.0", ] [[package]] name = "windows" -version = "0.61.3" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" dependencies = [ - "windows-collections", - "windows-core 0.61.2", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] @@ -13521,27 +13501,23 @@ dependencies = [ ] [[package]] -name = "windows-core" -version = "0.57.0" +name = "windows-collections" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" dependencies = [ - "windows-implement 0.57.0", - "windows-interface 0.57.0", - "windows-result 0.1.2", - "windows-targets 0.52.6", + "windows-core 0.62.2", ] [[package]] name = "windows-core" -version = "0.58.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", "windows-targets 0.52.6", ] @@ -13551,8 +13527,8 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.60.1", - "windows-interface 0.59.2", + "windows-implement 0.60.2", + "windows-interface 0.59.3", "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", @@ -13560,15 +13536,15 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.62.1" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6844ee5416b285084d3d3fffd743b925a6c9385455f64f6d4fa3031c4c2749a9" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows-implement 0.60.1", - "windows-interface 0.59.2", - "windows-link 0.2.0", - "windows-result 0.4.0", - "windows-strings 0.5.0", + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", ] [[package]] @@ -13579,40 +13555,40 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", ] [[package]] -name = "windows-implement" -version = "0.57.0" +name = "windows-future" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] name = "windows-implement" -version = "0.58.0" +version = "0.57.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] name = "windows-implement" -version = "0.60.1" +version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -13623,29 +13599,18 @@ checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] name = "windows-interface" -version = "0.58.0" +version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", -] - -[[package]] -name = "windows-interface" -version = "0.59.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -13656,9 +13621,9 @@ checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-link" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-numerics" @@ -13671,19 +13636,20 @@ dependencies = [ ] [[package]] -name = "windows-result" -version = "0.1.2" +name = "windows-numerics" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-targets 0.52.6", + "windows-core 0.62.2", + "windows-link 0.2.1", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" dependencies = [ "windows-targets 0.52.6", ] @@ -13699,21 +13665,11 @@ dependencies = [ [[package]] name = "windows-result" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" -dependencies = [ - "windows-link 0.2.0", -] - -[[package]] -name = "windows-strings" -version = "0.1.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-link 0.2.1", ] [[package]] @@ -13727,11 +13683,11 @@ dependencies = [ [[package]] name = "windows-strings" -version = "0.5.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -13776,16 +13732,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.4", + "windows-targets 0.53.5", ] [[package]] name = "windows-sys" -version = "0.61.1" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.0", + "windows-link 0.2.1", ] [[package]] @@ -13836,19 +13792,19 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.4" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.0", - "windows_aarch64_gnullvm 0.53.0", - "windows_aarch64_msvc 0.53.0", - "windows_i686_gnu 0.53.0", - "windows_i686_gnullvm 0.53.0", - "windows_i686_msvc 0.53.0", - "windows_x86_64_gnu 0.53.0", - "windows_x86_64_gnullvm 0.53.0", - "windows_x86_64_msvc 0.53.0", + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -13860,6 +13816,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -13880,9 +13845,9 @@ checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -13904,9 +13869,9 @@ checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_aarch64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -13928,9 +13893,9 @@ checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" [[package]] name = "windows_i686_gnullvm" @@ -13940,9 +13905,9 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -13964,9 +13929,9 @@ checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_i686_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -13988,9 +13953,9 @@ checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnu" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -14012,9 +13977,9 @@ checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_gnullvm" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -14036,9 +14001,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "windows_x86_64_msvc" -version = "0.53.0" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" @@ -14096,7 +14061,7 @@ dependencies = [ "pharos", "rustc_version 0.4.1", "send_wrapper 0.6.0", - "thiserror 2.0.16", + "thiserror 2.0.17", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -14159,7 +14124,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "synstructure", ] @@ -14171,7 +14136,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "synstructure", ] @@ -14192,7 +14157,7 @@ checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -14212,15 +14177,15 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", "synstructure", ] [[package]] name = "zeroize" -version = "1.8.1" +version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" dependencies = [ "zeroize_derive", ] @@ -14233,7 +14198,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -14277,7 +14242,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] @@ -14288,7 +14253,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.106", + "syn 2.0.107", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 7d2527a17b2..609fd09b7b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -450,7 +450,7 @@ reth-rpc-convert = { path = "crates/rpc/rpc-convert" } reth-stages = { path = "crates/stages/stages" } reth-stages-api = { path = "crates/stages/api" } reth-stages-types = { path = "crates/stages/types", default-features = false } -reth-stateless = { path = "crates/stateless" } +reth-stateless = { path = "crates/stateless", default-features = false } reth-static-file = { path = "crates/static-file/static-file" } reth-static-file-types = { path = "crates/static-file/types", default-features = false } reth-storage-api = { path = "crates/storage/storage-api", default-features = false } @@ -472,68 +472,68 @@ reth-ress-protocol = { path = "crates/ress/protocol" } reth-ress-provider = { path = "crates/ress/provider" } # revm -revm = { version = "29.0.1", default-features = false } -revm-bytecode = { version = "6.2.2", default-features = false } -revm-database = { version = "7.0.5", default-features = false } -revm-state = { version = "7.0.5", default-features = false } -revm-primitives = { version = "20.2.1", default-features = false } -revm-interpreter = { version = "25.0.3", default-features = false } -revm-inspector = { version = "10.0.1", default-features = false } -revm-context = { version = "9.1.0", default-features = false } -revm-context-interface = { version = "10.2.0", default-features = false } -revm-database-interface = { version = "7.0.5", default-features = false } -op-revm = { version = "10.1.0", default-features = false } -revm-inspectors = "0.30.0" +revm = { version = "30.1.1", default-features = false } +revm-bytecode = { version = "7.0.2", default-features = false } +revm-database = { version = "9.0.0", default-features = false } +revm-state = { version = "8.0.0", default-features = false } +revm-primitives = { version = "21.0.0", default-features = false } +revm-interpreter = { version = "27.0.0", default-features = false } +revm-inspector = { version = "11.1.0", default-features = false } +revm-context = { version = "10.1.0", default-features = false } +revm-context-interface = { version = "11.1.0", default-features = false } +revm-database-interface = { version = "8.0.1", default-features = false } +op-revm = { version = "11.1.0", default-features = false } +revm-inspectors = "0.31.0" # eth alloy-chains = { version = "0.2.5", default-features = false } alloy-dyn-abi = "1.4.1" alloy-eip2124 = { version = "0.2.0", default-features = false } -alloy-evm = { version = "0.21.2", default-features = false } +alloy-evm = { version = "0.22.0", default-features = false } alloy-primitives = { version = "1.4.1", default-features = false, features = ["map-foldhash"] } alloy-rlp = { version = "0.3.10", default-features = false, features = ["core-net"] } alloy-sol-macro = "1.4.1" alloy-sol-types = { version = "1.4.1", default-features = false } alloy-trie = { version = "0.9.1", default-features = false } -alloy-hardforks = "0.3.5" - -alloy-consensus = { version = "1.0.37", default-features = false } -alloy-contract = { version = "1.0.37", default-features = false } -alloy-eips = { version = "1.0.37", default-features = false } -alloy-genesis = { version = "1.0.37", default-features = false } -alloy-json-rpc = { version = "1.0.37", default-features = false } -alloy-network = { version = "1.0.37", default-features = false } -alloy-network-primitives = { version = "1.0.37", default-features = false } -alloy-provider = { version = "1.0.37", features = ["reqwest"], default-features = false } -alloy-pubsub = { version = "1.0.37", default-features = false } -alloy-rpc-client = { version = "1.0.37", default-features = false } -alloy-rpc-types = { version = "1.0.37", features = ["eth"], default-features = false } -alloy-rpc-types-admin = { version = "1.0.37", default-features = false } -alloy-rpc-types-anvil = { version = "1.0.37", default-features = false } -alloy-rpc-types-beacon = { version = "1.0.37", default-features = false } -alloy-rpc-types-debug = { version = "1.0.37", default-features = false } -alloy-rpc-types-engine = { version = "1.0.37", default-features = false } -alloy-rpc-types-eth = { version = "1.0.37", default-features = false } -alloy-rpc-types-mev = { version = "1.0.37", default-features = false } -alloy-rpc-types-trace = { version = "1.0.37", default-features = false } -alloy-rpc-types-txpool = { version = "1.0.37", default-features = false } -alloy-serde = { version = "1.0.37", default-features = false } -alloy-signer = { version = "1.0.37", default-features = false } -alloy-signer-local = { version = "1.0.37", default-features = false } -alloy-transport = { version = "1.0.37" } -alloy-transport-http = { version = "1.0.37", features = ["reqwest-rustls-tls"], default-features = false } -alloy-transport-ipc = { version = "1.0.37", default-features = false } -alloy-transport-ws = { version = "1.0.37", default-features = false } +alloy-hardforks = "0.4.0" + +alloy-consensus = { version = "1.0.41", default-features = false } +alloy-contract = { version = "1.0.41", default-features = false } +alloy-eips = { version = "1.0.41", default-features = false } +alloy-genesis = { version = "1.0.41", default-features = false } +alloy-json-rpc = { version = "1.0.41", default-features = false } +alloy-network = { version = "1.0.41", default-features = false } +alloy-network-primitives = { version = "1.0.41", default-features = false } +alloy-provider = { version = "1.0.41", features = ["reqwest"], default-features = false } +alloy-pubsub = { version = "1.0.41", default-features = false } +alloy-rpc-client = { version = "1.0.41", default-features = false } +alloy-rpc-types = { version = "1.0.41", features = ["eth"], default-features = false } +alloy-rpc-types-admin = { version = "1.0.41", default-features = false } +alloy-rpc-types-anvil = { version = "1.0.41", default-features = false } +alloy-rpc-types-beacon = { version = "1.0.41", default-features = false } +alloy-rpc-types-debug = { version = "1.0.41", default-features = false } +alloy-rpc-types-engine = { version = "1.0.41", default-features = false } +alloy-rpc-types-eth = { version = "1.0.41", default-features = false } +alloy-rpc-types-mev = { version = "1.0.41", default-features = false } +alloy-rpc-types-trace = { version = "1.0.41", default-features = false } +alloy-rpc-types-txpool = { version = "1.0.41", default-features = false } +alloy-serde = { version = "1.0.41", default-features = false } +alloy-signer = { version = "1.0.41", default-features = false } +alloy-signer-local = { version = "1.0.41", default-features = false } +alloy-transport = { version = "1.0.41" } +alloy-transport-http = { version = "1.0.41", features = ["reqwest-rustls-tls"], default-features = false } +alloy-transport-ipc = { version = "1.0.41", default-features = false } +alloy-transport-ws = { version = "1.0.41", default-features = false } # op -alloy-op-evm = { version = "0.21.2", default-features = false } -alloy-op-hardforks = "0.3.5" -op-alloy-rpc-types = { version = "0.20.0", default-features = false } -op-alloy-rpc-types-engine = { version = "0.20.0", default-features = false } -op-alloy-network = { version = "0.20.0", default-features = false } -op-alloy-consensus = { version = "0.20.0", default-features = false } -op-alloy-rpc-jsonrpsee = { version = "0.20.0", default-features = false } +alloy-op-evm = { version = "0.22.0", default-features = false } +alloy-op-hardforks = "0.4.0" +op-alloy-rpc-types = { version = "0.21.0", default-features = false } +op-alloy-rpc-types-engine = { version = "0.21.0", default-features = false } +op-alloy-network = { version = "0.21.0", default-features = false } +op-alloy-consensus = { version = "0.21.0", default-features = false } +op-alloy-rpc-jsonrpsee = { version = "0.21.0", default-features = false } op-alloy-flz = { version = "0.13.1", default-features = false } # misc @@ -554,7 +554,8 @@ dirs-next = "2.0.0" dyn-clone = "1.0.17" eyre = "0.6" fdlimit = "0.3.0" -generic-array = "0.14" +# pinned until downstream crypto libs migrate to 1.0 because 0.14.8 marks all types as deprecated +generic-array = "=0.14.7" humantime = "2.1" humantime-serde = "1.1" itertools = { version = "0.14", default-features = false } diff --git a/crates/chain-state/Cargo.toml b/crates/chain-state/Cargo.toml index cba12995015..d21c83ae7c4 100644 --- a/crates/chain-state/Cargo.toml +++ b/crates/chain-state/Cargo.toml @@ -54,6 +54,7 @@ reth-testing-utils.workspace = true alloy-signer.workspace = true alloy-signer-local.workspace = true rand.workspace = true +revm-state.workspace = true criterion.workspace = true [features] diff --git a/crates/chain-state/benches/canonical_hashes_range.rs b/crates/chain-state/benches/canonical_hashes_range.rs index 58fdd73bf99..c19ce25ec4f 100644 --- a/crates/chain-state/benches/canonical_hashes_range.rs +++ b/crates/chain-state/benches/canonical_hashes_range.rs @@ -2,7 +2,7 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion}; use reth_chain_state::{ - test_utils::TestBlockBuilder, ExecutedBlockWithTrieUpdates, MemoryOverlayStateProviderRef, + test_utils::TestBlockBuilder, ExecutedBlock, MemoryOverlayStateProviderRef, }; use reth_ethereum_primitives::EthPrimitives; use reth_storage_api::{noop::NoopProvider, BlockHashReader}; @@ -84,10 +84,7 @@ fn bench_canonical_hashes_range(c: &mut Criterion) { fn setup_provider_with_blocks( num_blocks: usize, -) -> ( - MemoryOverlayStateProviderRef<'static, EthPrimitives>, - Vec>, -) { +) -> (MemoryOverlayStateProviderRef<'static, EthPrimitives>, Vec>) { let mut builder = TestBlockBuilder::::default(); let blocks: Vec<_> = builder.get_executed_blocks(1000..1000 + num_blocks as u64).collect(); diff --git a/crates/chain-state/src/chain_info.rs b/crates/chain-state/src/chain_info.rs index a8a08430566..dd6afc8db1a 100644 --- a/crates/chain-state/src/chain_info.rs +++ b/crates/chain-state/src/chain_info.rs @@ -77,22 +77,22 @@ where self.inner.finalized_block.borrow().clone() } - /// Returns the canonical head of the chain. + /// Returns the `BlockNumHash` of the canonical head. pub fn get_canonical_num_hash(&self) -> BlockNumHash { self.inner.canonical_head.read().num_hash() } - /// Returns the canonical head of the chain. + /// Returns the block number of the canonical head. pub fn get_canonical_block_number(&self) -> BlockNumber { self.inner.canonical_head_number.load(Ordering::Relaxed) } - /// Returns the safe header of the chain. + /// Returns the `BlockNumHash` of the safe header. pub fn get_safe_num_hash(&self) -> Option { self.inner.safe_block.borrow().as_ref().map(SealedHeader::num_hash) } - /// Returns the finalized header of the chain. + /// Returns the `BlockNumHash` of the finalized header. pub fn get_finalized_num_hash(&self) -> Option { self.inner.finalized_block.borrow().as_ref().map(SealedHeader::num_hash) } diff --git a/crates/chain-state/src/in_memory.rs b/crates/chain-state/src/in_memory.rs index dd78b6cf5fe..a6c85538107 100644 --- a/crates/chain-state/src/in_memory.rs +++ b/crates/chain-state/src/in_memory.rs @@ -242,7 +242,7 @@ impl CanonicalInMemoryState { /// Updates the pending block with the given block. /// /// Note: This assumes that the parent block of the pending block is canonical. - pub fn set_pending_block(&self, pending: ExecutedBlockWithTrieUpdates) { + pub fn set_pending_block(&self, pending: ExecutedBlock) { // fetch the state of the pending block's parent block let parent = self.state_by_hash(pending.recovered_block().parent_hash()); let pending = BlockState::with_parent(pending, parent); @@ -258,7 +258,7 @@ impl CanonicalInMemoryState { /// them to their parent blocks. fn update_blocks(&self, new_blocks: I, reorged: R) where - I: IntoIterator>, + I: IntoIterator>, R: IntoIterator>, { { @@ -568,22 +568,19 @@ impl CanonicalInMemoryState { #[derive(Debug, PartialEq, Eq, Clone)] pub struct BlockState { /// The executed block that determines the state after this block has been executed. - block: ExecutedBlockWithTrieUpdates, + block: ExecutedBlock, /// The block's parent block if it exists. - parent: Option>>, + parent: Option>, } impl BlockState { /// [`BlockState`] constructor. - pub const fn new(block: ExecutedBlockWithTrieUpdates) -> Self { + pub const fn new(block: ExecutedBlock) -> Self { Self { block, parent: None } } /// [`BlockState`] constructor with parent. - pub const fn with_parent( - block: ExecutedBlockWithTrieUpdates, - parent: Option>, - ) -> Self { + pub const fn with_parent(block: ExecutedBlock, parent: Option>) -> Self { Self { block, parent } } @@ -597,12 +594,12 @@ impl BlockState { } /// Returns the executed block that determines the state. - pub fn block(&self) -> ExecutedBlockWithTrieUpdates { + pub fn block(&self) -> ExecutedBlock { self.block.clone() } /// Returns a reference to the executed block that determines the state. - pub const fn block_ref(&self) -> &ExecutedBlockWithTrieUpdates { + pub const fn block_ref(&self) -> &ExecutedBlock { &self.block } @@ -730,6 +727,8 @@ pub struct ExecutedBlock { pub execution_output: Arc>, /// Block's hashed state. pub hashed_state: Arc, + /// Trie updates that result from calculating the state root for the block. + pub trie_updates: Arc, } impl Default for ExecutedBlock { @@ -738,6 +737,7 @@ impl Default for ExecutedBlock { recovered_block: Default::default(), execution_output: Default::default(), hashed_state: Default::default(), + trie_updates: Default::default(), } } } @@ -767,113 +767,16 @@ impl ExecutedBlock { &self.hashed_state } - /// Returns a [`BlockNumber`] of the block. + /// Returns a reference to the trie updates resulting from the execution outcome #[inline] - pub fn block_number(&self) -> BlockNumber { - self.recovered_block.header().number() + pub fn trie_updates(&self) -> &TrieUpdates { + &self.trie_updates } -} -/// Trie updates that result from calculating the state root for the block. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum ExecutedTrieUpdates { - /// Trie updates present. State root was calculated, and the trie updates can be applied to the - /// database. - Present(Arc), - /// Trie updates missing. State root was calculated, but the trie updates cannot be applied to - /// the current database state. To apply the updates, the state root must be recalculated, and - /// new trie updates must be generated. - /// - /// This can happen when processing fork chain blocks that are building on top of the - /// historical database state. Since we don't store the historical trie state, we cannot - /// generate the trie updates for it. - Missing, -} - -impl ExecutedTrieUpdates { - /// Creates a [`ExecutedTrieUpdates`] with present but empty trie updates. - pub fn empty() -> Self { - Self::Present(Arc::default()) - } - - /// Sets the trie updates to the provided value as present. - pub fn set_present(&mut self, updates: Arc) { - *self = Self::Present(updates); - } - - /// Takes the present trie updates, leaving the state as missing. - pub fn take_present(&mut self) -> Option> { - match self { - Self::Present(updates) => { - let updates = core::mem::take(updates); - *self = Self::Missing; - Some(updates) - } - Self::Missing => None, - } - } - - /// Returns a reference to the trie updates if present. - #[allow(clippy::missing_const_for_fn)] // false positive - pub fn as_ref(&self) -> Option<&TrieUpdates> { - match self { - Self::Present(updates) => Some(updates), - Self::Missing => None, - } - } - - /// Returns `true` if the trie updates are present. - pub const fn is_present(&self) -> bool { - matches!(self, Self::Present(_)) - } - - /// Returns `true` if the trie updates are missing. - pub const fn is_missing(&self) -> bool { - matches!(self, Self::Missing) - } -} - -/// An [`ExecutedBlock`] with its [`TrieUpdates`]. -/// -/// We store it as separate type because [`TrieUpdates`] are only available for blocks stored in -/// memory and can't be obtained for canonical persisted blocks. -#[derive( - Clone, Debug, PartialEq, Eq, derive_more::Deref, derive_more::DerefMut, derive_more::Into, -)] -pub struct ExecutedBlockWithTrieUpdates { - /// Inner [`ExecutedBlock`]. - #[deref] - #[deref_mut] - #[into] - pub block: ExecutedBlock, - /// Trie updates that result from calculating the state root for the block. - /// - /// If [`ExecutedTrieUpdates::Missing`], the trie updates should be computed when persisting - /// the block **on top of the canonical parent**. - pub trie: ExecutedTrieUpdates, -} - -impl ExecutedBlockWithTrieUpdates { - /// [`ExecutedBlock`] constructor. - pub const fn new( - recovered_block: Arc>, - execution_output: Arc>, - hashed_state: Arc, - trie: ExecutedTrieUpdates, - ) -> Self { - Self { block: ExecutedBlock { recovered_block, execution_output, hashed_state }, trie } - } - - /// Returns a reference to the trie updates for the block, if present. + /// Returns a [`BlockNumber`] of the block. #[inline] - pub fn trie_updates(&self) -> Option<&TrieUpdates> { - self.trie.as_ref() - } - - /// Converts the value into [`SealedBlock`]. - pub fn into_sealed_block(self) -> SealedBlock { - let block = Arc::unwrap_or_clone(self.block.recovered_block); - block.into_sealed_block() + pub fn block_number(&self) -> BlockNumber { + self.recovered_block.header().number() } } @@ -883,18 +786,14 @@ pub enum NewCanonicalChain { /// A simple append to the current canonical head Commit { /// all blocks that lead back to the canonical head - new: Vec>, + new: Vec>, }, /// A reorged chain consists of two chains that trace back to a shared ancestor block at which /// point they diverge. Reorg { /// All blocks of the _new_ chain - new: Vec>, + new: Vec>, /// All blocks of the _old_ chain - /// - /// These are not [`ExecutedBlockWithTrieUpdates`] because we don't always have the trie - /// updates for the old canonical chain. For example, in case of node being restarted right - /// before the reorg [`TrieUpdates`] can't be fetched from database. old: Vec>, }, } @@ -1257,7 +1156,7 @@ mod tests { block1.recovered_block().hash() ); - let chain = NewCanonicalChain::Reorg { new: vec![block2.clone()], old: vec![block1.block] }; + let chain = NewCanonicalChain::Reorg { new: vec![block2.clone()], old: vec![block1] }; state.update_chain(chain); assert_eq!( state.head_state().unwrap().block_ref().recovered_block().hash(), @@ -1539,7 +1438,7 @@ mod tests { // Test reorg notification let chain_reorg = NewCanonicalChain::Reorg { new: vec![block1a.clone(), block2a.clone()], - old: vec![block1.block.clone(), block2.block.clone()], + old: vec![block1.clone(), block2.clone()], }; assert_eq!( diff --git a/crates/chain-state/src/memory_overlay.rs b/crates/chain-state/src/memory_overlay.rs index a035d833a46..254edb248b4 100644 --- a/crates/chain-state/src/memory_overlay.rs +++ b/crates/chain-state/src/memory_overlay.rs @@ -1,4 +1,4 @@ -use super::ExecutedBlockWithTrieUpdates; +use super::ExecutedBlock; use alloy_consensus::BlockHeader; use alloy_primitives::{keccak256, Address, BlockNumber, Bytes, StorageKey, StorageValue, B256}; use reth_errors::ProviderResult; @@ -24,7 +24,7 @@ pub struct MemoryOverlayStateProviderRef< /// Historical state provider for state lookups that are not found in memory blocks. pub(crate) historical: Box, /// The collection of executed parent blocks. Expected order is newest to oldest. - pub(crate) in_memory: Vec>, + pub(crate) in_memory: Vec>, /// Lazy-loaded in-memory trie data. pub(crate) trie_input: OnceLock, } @@ -41,10 +41,7 @@ impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> { /// - `in_memory` - the collection of executed ancestor blocks in reverse. /// - `historical` - a historical state provider for the latest ancestor block stored in the /// database. - pub fn new( - historical: Box, - in_memory: Vec>, - ) -> Self { + pub fn new(historical: Box, in_memory: Vec>) -> Self { Self { historical, in_memory, trie_input: OnceLock::new() } } @@ -60,10 +57,17 @@ impl<'a, N: NodePrimitives> MemoryOverlayStateProviderRef<'a, N> { self.in_memory .iter() .rev() - .map(|block| (block.hashed_state.as_ref(), block.trie.as_ref())), + .map(|block| (block.hashed_state.as_ref(), block.trie_updates.as_ref())), ) }) } + + fn merged_hashed_storage(&self, address: Address, storage: HashedStorage) -> HashedStorage { + let state = &self.trie_input().state; + let mut hashed = state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); + hashed.extend(&storage); + hashed + } } impl BlockHashReader for MemoryOverlayStateProviderRef<'_, N> { @@ -148,11 +152,8 @@ impl StateRootProvider for MemoryOverlayStateProviderRef<'_, impl StorageRootProvider for MemoryOverlayStateProviderRef<'_, N> { // TODO: Currently this does not reuse available in-memory trie nodes. fn storage_root(&self, address: Address, storage: HashedStorage) -> ProviderResult { - let state = &self.trie_input().state; - let mut hashed_storage = - state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); - hashed_storage.extend(&storage); - self.historical.storage_root(address, hashed_storage) + let merged = self.merged_hashed_storage(address, storage); + self.historical.storage_root(address, merged) } // TODO: Currently this does not reuse available in-memory trie nodes. @@ -162,11 +163,8 @@ impl StorageRootProvider for MemoryOverlayStateProviderRef<'_ slot: B256, storage: HashedStorage, ) -> ProviderResult { - let state = &self.trie_input().state; - let mut hashed_storage = - state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); - hashed_storage.extend(&storage); - self.historical.storage_proof(address, slot, hashed_storage) + let merged = self.merged_hashed_storage(address, storage); + self.historical.storage_proof(address, slot, merged) } // TODO: Currently this does not reuse available in-memory trie nodes. @@ -176,11 +174,8 @@ impl StorageRootProvider for MemoryOverlayStateProviderRef<'_ slots: &[B256], storage: HashedStorage, ) -> ProviderResult { - let state = &self.trie_input().state; - let mut hashed_storage = - state.storages.get(&keccak256(address)).cloned().unwrap_or_default(); - hashed_storage.extend(&storage); - self.historical.storage_multiproof(address, slots, hashed_storage) + let merged = self.merged_hashed_storage(address, storage); + self.historical.storage_multiproof(address, slots, merged) } } diff --git a/crates/chain-state/src/test_utils.rs b/crates/chain-state/src/test_utils.rs index ace30b9cb35..5d318aca56c 100644 --- a/crates/chain-state/src/test_utils.rs +++ b/crates/chain-state/src/test_utils.rs @@ -1,6 +1,6 @@ use crate::{ - in_memory::ExecutedBlockWithTrieUpdates, CanonStateNotification, CanonStateNotifications, - CanonStateSubscriptions, ExecutedTrieUpdates, + in_memory::ExecutedBlock, CanonStateNotification, CanonStateNotifications, + CanonStateSubscriptions, }; use alloy_consensus::{Header, SignableTransaction, TxEip1559, TxReceipt, EMPTY_ROOT_HASH}; use alloy_eips::{ @@ -23,7 +23,7 @@ use reth_primitives_traits::{ SignedTransaction, }; use reth_storage_api::NodePrimitivesProvider; -use reth_trie::{root::state_root_unhashed, HashedPostState}; +use reth_trie::{root::state_root_unhashed, updates::TrieUpdates, HashedPostState}; use revm_database::BundleState; use revm_state::AccountInfo; use std::{ @@ -198,45 +198,45 @@ impl TestBlockBuilder { fork } - /// Gets an [`ExecutedBlockWithTrieUpdates`] with [`BlockNumber`], receipts and parent hash. + /// Gets an [`ExecutedBlock`] with [`BlockNumber`], receipts and parent hash. fn get_executed_block( &mut self, block_number: BlockNumber, receipts: Vec>, parent_hash: B256, - ) -> ExecutedBlockWithTrieUpdates { + ) -> ExecutedBlock { let block_with_senders = self.generate_random_block(block_number, parent_hash); let (block, senders) = block_with_senders.split_sealed(); - ExecutedBlockWithTrieUpdates::new( - Arc::new(RecoveredBlock::new_sealed(block, senders)), - Arc::new(ExecutionOutcome::new( + ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed(block, senders)), + execution_output: Arc::new(ExecutionOutcome::new( BundleState::default(), receipts, block_number, vec![Requests::default()], )), - Arc::new(HashedPostState::default()), - ExecutedTrieUpdates::empty(), - ) + hashed_state: Arc::new(HashedPostState::default()), + trie_updates: Arc::new(TrieUpdates::default()), + } } - /// Generates an [`ExecutedBlockWithTrieUpdates`] that includes the given receipts. + /// Generates an [`ExecutedBlock`] that includes the given receipts. pub fn get_executed_block_with_receipts( &mut self, receipts: Vec>, parent_hash: B256, - ) -> ExecutedBlockWithTrieUpdates { + ) -> ExecutedBlock { let number = rand::rng().random::(); self.get_executed_block(number, receipts, parent_hash) } - /// Generates an [`ExecutedBlockWithTrieUpdates`] with the given [`BlockNumber`]. + /// Generates an [`ExecutedBlock`] with the given [`BlockNumber`]. pub fn get_executed_block_with_number( &mut self, block_number: BlockNumber, parent_hash: B256, - ) -> ExecutedBlockWithTrieUpdates { + ) -> ExecutedBlock { self.get_executed_block(block_number, vec![vec![]], parent_hash) } @@ -244,7 +244,7 @@ impl TestBlockBuilder { pub fn get_executed_blocks( &mut self, range: Range, - ) -> impl Iterator + '_ { + ) -> impl Iterator + '_ { let mut parent_hash = B256::default(); range.map(move |number| { let current_parent_hash = parent_hash; diff --git a/crates/chainspec/src/spec.rs b/crates/chainspec/src/spec.rs index 88e5a370d6d..a0cccfcc449 100644 --- a/crates/chainspec/src/spec.rs +++ b/crates/chainspec/src/spec.rs @@ -3,7 +3,7 @@ use alloy_evm::eth::spec::EthExecutorSpec; use crate::{ constants::{MAINNET_DEPOSIT_CONTRACT, MAINNET_PRUNE_DELETE_LIMIT}, - holesky, hoodi, mainnet, sepolia, EthChainSpec, + holesky, hoodi, sepolia, EthChainSpec, }; use alloc::{boxed::Box, sync::Arc, vec::Vec}; use alloy_chains::{Chain, NamedChain}; @@ -108,10 +108,7 @@ pub static MAINNET: LazyLock> = LazyLock::new(|| { deposit_contract: Some(MAINNET_DEPOSIT_CONTRACT), base_fee_params: BaseFeeParamsKind::Constant(BaseFeeParams::ethereum()), prune_delete_limit: MAINNET_PRUNE_DELETE_LIMIT, - blob_params: BlobScheduleBlobParams::default().with_scheduled([ - (mainnet::MAINNET_BPO1_TIMESTAMP, BlobParams::bpo1()), - (mainnet::MAINNET_BPO2_TIMESTAMP, BlobParams::bpo2()), - ]), + blob_params: BlobScheduleBlobParams::default(), }; spec.genesis.config.dao_fork_support = true; spec.into() @@ -1129,10 +1126,7 @@ Merge hard forks: Post-merge hard forks (timestamp based): - Shanghai @1681338455 - Cancun @1710338135 -- Prague @1746612311 -- Osaka @1764798551 -- Bpo1 @1765978199 -- Bpo2 @1767747671" +- Prague @1746612311" ); } @@ -1376,10 +1370,7 @@ Post-merge hard forks (timestamp based): ), ( EthereumHardfork::Prague, - ForkId { - hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), - next: mainnet::MAINNET_OSAKA_TIMESTAMP, - }, + ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 }, ), ], ); @@ -1523,22 +1514,12 @@ Post-merge hard forks (timestamp based): // First Prague block ( Head { number: 20000002, timestamp: 1746612311, ..Default::default() }, - ForkId { - hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), - next: mainnet::MAINNET_OSAKA_TIMESTAMP, - }, + ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 }, ), - // Osaka block + // Future Prague block ( - Head { - number: 20000002, - timestamp: mainnet::MAINNET_OSAKA_TIMESTAMP, - ..Default::default() - }, - ForkId { - hash: ForkHash(hex!("0x5167e2a6")), - next: mainnet::MAINNET_BPO1_TIMESTAMP, - }, + Head { number: 20000002, timestamp: 2000000000, ..Default::default() }, + ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 }, ), ], ); @@ -1847,22 +1828,11 @@ Post-merge hard forks (timestamp based): ), // First Prague block ( Head { number: 20000004, timestamp: 1746612311, ..Default::default() }, - ForkId { - hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), - next: mainnet::MAINNET_OSAKA_TIMESTAMP, - }, - ), - // Osaka block + ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 }, + ), // Future Prague block ( - Head { - number: 20000004, - timestamp: mainnet::MAINNET_OSAKA_TIMESTAMP, - ..Default::default() - }, - ForkId { - hash: ForkHash(hex!("0x5167e2a6")), - next: mainnet::MAINNET_BPO1_TIMESTAMP, - }, + Head { number: 20000004, timestamp: 2000000000, ..Default::default() }, + ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 }, ), ], ); @@ -2519,8 +2489,10 @@ Post-merge hard forks (timestamp based): #[test] fn latest_eth_mainnet_fork_id() { - // BPO2 - assert_eq!(ForkId { hash: ForkHash(hex!("0xfd414558")), next: 0 }, MAINNET.latest_fork_id()) + assert_eq!( + ForkId { hash: ForkHash([0xc3, 0x76, 0xcf, 0x8b]), next: 0 }, + MAINNET.latest_fork_id() + ) } #[test] diff --git a/crates/cli/commands/Cargo.toml b/crates/cli/commands/Cargo.toml index 242cc6d5d9d..da1a5318f25 100644 --- a/crates/cli/commands/Cargo.toml +++ b/crates/cli/commands/Cargo.toml @@ -99,6 +99,8 @@ proptest-arbitrary-interop = { workspace = true, optional = true } [dev-dependencies] reth-ethereum-cli.workspace = true +reth-provider = { workspace = true, features = ["test-utils"] } +tempfile.workspace = true [features] default = [] diff --git a/crates/cli/commands/src/db/get.rs b/crates/cli/commands/src/db/get.rs index 6214df0ec98..9d06a35dcaa 100644 --- a/crates/cli/commands/src/db/get.rs +++ b/crates/cli/commands/src/db/get.rs @@ -1,4 +1,3 @@ -use alloy_consensus::Header; use alloy_primitives::{hex, BlockHash}; use clap::Parser; use reth_db::{ @@ -66,9 +65,10 @@ impl Command { } Subcommand::StaticFile { segment, key, raw } => { let (key, mask): (u64, _) = match segment { - StaticFileSegment::Headers => { - (table_key::(&key)?, >::MASK) - } + StaticFileSegment::Headers => ( + table_key::(&key)?, + >>::MASK, + ), StaticFileSegment::Transactions => { (table_key::(&key)?, >>::MASK) } diff --git a/crates/cli/commands/src/import_core.rs b/crates/cli/commands/src/import_core.rs index 2370ebaa039..98f888bb9e3 100644 --- a/crates/cli/commands/src/import_core.rs +++ b/crates/cli/commands/src/import_core.rs @@ -102,6 +102,9 @@ where .sealed_header(provider_factory.last_block_number()?)? .expect("should have genesis"); + let static_file_producer = + StaticFileProducer::new(provider_factory.clone(), PruneModes::default()); + while let Some(file_client) = reader.next_chunk::>(consensus.clone(), Some(sealed_header)).await? { @@ -121,7 +124,7 @@ where provider_factory.clone(), &consensus, Arc::new(file_client), - StaticFileProducer::new(provider_factory.clone(), PruneModes::default()), + static_file_producer.clone(), import_config.no_state, executor.clone(), )?; diff --git a/crates/cli/commands/src/init_state/mod.rs b/crates/cli/commands/src/init_state/mod.rs index 68618361e7f..4b5c51585b3 100644 --- a/crates/cli/commands/src/init_state/mod.rs +++ b/crates/cli/commands/src/init_state/mod.rs @@ -2,7 +2,7 @@ use crate::common::{AccessRights, CliHeader, CliNodeTypes, Environment, EnvironmentArgs}; use alloy_consensus::BlockHeader as AlloyBlockHeader; -use alloy_primitives::{B256, U256}; +use alloy_primitives::{Sealable, B256}; use clap::Parser; use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_cli::chainspec::ChainSpecParser; @@ -13,7 +13,7 @@ use reth_provider::{ BlockNumReader, DBProvider, DatabaseProviderFactory, StaticFileProviderFactory, StaticFileWriter, }; -use std::{io::BufReader, path::PathBuf, str::FromStr, sync::Arc}; +use std::{io::BufReader, path::PathBuf, sync::Arc}; use tracing::info; pub mod without_evm; @@ -58,13 +58,9 @@ pub struct InitStateCommand { #[arg(long, value_name = "HEADER_FILE", verbatim_doc_comment)] pub header: Option, - /// Total difficulty of the header. - #[arg(long, value_name = "TOTAL_DIFFICULTY", verbatim_doc_comment)] - pub total_difficulty: Option, - /// Hash of the header. #[arg(long, value_name = "HEADER_HASH", verbatim_doc_comment)] - pub header_hash: Option, + pub header_hash: Option, } impl> InitStateCommand { @@ -88,16 +84,9 @@ impl> InitStateC let header = self.header.ok_or_else(|| eyre::eyre!("Header file must be provided"))?; let header = without_evm::read_header_from_file::< ::BlockHeader, - >(header)?; - - let header_hash = - self.header_hash.ok_or_else(|| eyre::eyre!("Header hash must be provided"))?; - let header_hash = B256::from_str(&header_hash)?; + >(&header)?; - let total_difficulty = self - .total_difficulty - .ok_or_else(|| eyre::eyre!("Total difficulty must be provided"))?; - let total_difficulty = U256::from_str(&total_difficulty)?; + let header_hash = self.header_hash.unwrap_or_else(|| header.hash_slow()); let last_block_number = provider_rw.last_block_number()?; @@ -105,7 +94,6 @@ impl> InitStateC without_evm::setup_without_evm( &provider_rw, SealedHeader::new(header, header_hash), - total_difficulty, |number| { let mut header = <::BlockHeader>::default(); @@ -146,3 +134,32 @@ impl InitStateCommand { Some(&self.env.chain) } } + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::b256; + use reth_ethereum_cli::chainspec::EthereumChainSpecParser; + + #[test] + fn parse_init_state_command_with_without_evm() { + let cmd: InitStateCommand = InitStateCommand::parse_from([ + "reth", + "--chain", + "sepolia", + "--without-evm", + "--header", + "header.rlp", + "--header-hash", + "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef", + "state.jsonl", + ]); + assert_eq!(cmd.state.to_str().unwrap(), "state.jsonl"); + assert!(cmd.without_evm); + assert_eq!(cmd.header.unwrap().to_str().unwrap(), "header.rlp"); + assert_eq!( + cmd.header_hash.unwrap(), + b256!("0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef") + ); + } +} diff --git a/crates/cli/commands/src/init_state/without_evm.rs b/crates/cli/commands/src/init_state/without_evm.rs index 09711d45880..de6320fc86e 100644 --- a/crates/cli/commands/src/init_state/without_evm.rs +++ b/crates/cli/commands/src/init_state/without_evm.rs @@ -10,16 +10,22 @@ use reth_provider::{ }; use reth_stages::{StageCheckpoint, StageId}; use reth_static_file_types::StaticFileSegment; -use std::{fs::File, io::Read, path::PathBuf}; +use std::path::Path; use tracing::info; + /// Reads the header RLP from a file and returns the Header. -pub(crate) fn read_header_from_file(path: PathBuf) -> Result +/// +/// This supports both raw rlp bytes and rlp hex string. +pub(crate) fn read_header_from_file(path: &Path) -> Result where H: Decodable, { - let mut file = File::open(path)?; - let mut buf = Vec::new(); - file.read_to_end(&mut buf)?; + let buf = if let Ok(content) = reth_fs_util::read_to_string(path) { + alloy_primitives::hex::decode(content.trim())? + } else { + // If UTF-8 decoding fails, read as raw bytes + reth_fs_util::read(path)? + }; let header = H::decode(&mut &buf[..])?; Ok(header) @@ -30,7 +36,6 @@ where pub fn setup_without_evm( provider_rw: &Provider, header: SealedHeader<::BlockHeader>, - total_difficulty: U256, header_factory: F, ) -> ProviderResult<()> where @@ -50,7 +55,7 @@ where info!(target: "reth::cli", "Appending first valid block."); - append_first_block(provider_rw, &header, total_difficulty)?; + append_first_block(provider_rw, &header)?; for stage in StageId::ALL { provider_rw.save_stage_checkpoint(stage, StageCheckpoint::new(header.number()))?; @@ -68,7 +73,6 @@ where fn append_first_block( provider_rw: &Provider, header: &SealedHeaderFor, - total_difficulty: U256, ) -> ProviderResult<()> where Provider: BlockWriter::Block> @@ -85,16 +89,8 @@ where let sf_provider = provider_rw.static_file_provider(); - sf_provider.latest_writer(StaticFileSegment::Headers)?.append_header( - header, - total_difficulty, - &header.hash(), - )?; - sf_provider.latest_writer(StaticFileSegment::Receipts)?.increment_block(header.number())?; - sf_provider.latest_writer(StaticFileSegment::Transactions)?.increment_block(header.number())?; - Ok(()) } @@ -167,3 +163,85 @@ where Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + use alloy_consensus::Header; + use alloy_primitives::{address, b256}; + use reth_db_common::init::init_genesis; + use reth_provider::{test_utils::create_test_provider_factory, DatabaseProviderFactory}; + use std::io::Write; + use tempfile::NamedTempFile; + + #[test] + fn test_read_header_from_file_hex_string() { + let header_rlp = "0xf90212a00d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479471562b71999873db5b286df957af199ec94617f7a0f496f3d199c51a1aaee67dac95f24d92ac13c60d25181e1eecd6eca5ddf32ac0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808206a4840365908a808468e975f09ad983011003846765746888676f312e32352e308664617277696ea06f485a167165ec12e0ab3e6ab59a7b88560b90306ac98a26eb294abf95a8c59b88000000000000000007"; + + let mut temp_file = NamedTempFile::new().unwrap(); + temp_file.write_all(header_rlp.as_bytes()).unwrap(); + temp_file.flush().unwrap(); + + let header: Header = read_header_from_file(temp_file.path()).unwrap(); + + assert_eq!(header.number, 1700); + assert_eq!( + header.parent_hash, + b256!("0d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dd") + ); + assert_eq!(header.beneficiary, address!("71562b71999873db5b286df957af199ec94617f7")); + } + + #[test] + fn test_read_header_from_file_raw_bytes() { + let header_rlp = "0xf90212a00d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479471562b71999873db5b286df957af199ec94617f7a0f496f3d199c51a1aaee67dac95f24d92ac13c60d25181e1eecd6eca5ddf32ac0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808206a4840365908a808468e975f09ad983011003846765746888676f312e32352e308664617277696ea06f485a167165ec12e0ab3e6ab59a7b88560b90306ac98a26eb294abf95a8c59b88000000000000000007"; + let header_bytes = + alloy_primitives::hex::decode(header_rlp.trim_start_matches("0x")).unwrap(); + + let mut temp_file = NamedTempFile::new().unwrap(); + temp_file.write_all(&header_bytes).unwrap(); + temp_file.flush().unwrap(); + + let header: Header = read_header_from_file(temp_file.path()).unwrap(); + + assert_eq!(header.number, 1700); + assert_eq!( + header.parent_hash, + b256!("0d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dd") + ); + assert_eq!(header.beneficiary, address!("71562b71999873db5b286df957af199ec94617f7")); + } + + #[test] + fn test_setup_without_evm_succeeds() { + let header_rlp = "0xf90212a00d84d79f59fc384a1f6402609a5b7253b4bfe7a4ae12608ed107273e5422b6dda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479471562b71999873db5b286df957af199ec94617f7a0f496f3d199c51a1aaee67dac95f24d92ac13c60d25181e1eecd6eca5ddf32ac0a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808206a4840365908a808468e975f09ad983011003846765746888676f312e32352e308664617277696ea06f485a167165ec12e0ab3e6ab59a7b88560b90306ac98a26eb294abf95a8c59b88000000000000000007"; + let header_bytes = + alloy_primitives::hex::decode(header_rlp.trim_start_matches("0x")).unwrap(); + + let mut temp_file = NamedTempFile::new().unwrap(); + temp_file.write_all(&header_bytes).unwrap(); + temp_file.flush().unwrap(); + + let header: Header = read_header_from_file(temp_file.path()).unwrap(); + let header_hash = b256!("4f05e4392969fc82e41f6d6a8cea379323b0b2d3ddf7def1a33eec03883e3a33"); + + let provider_factory = create_test_provider_factory(); + + init_genesis(&provider_factory).unwrap(); + + let provider_rw = provider_factory.database_provider_rw().unwrap(); + + setup_without_evm(&provider_rw, SealedHeader::new(header, header_hash), |number| Header { + number, + ..Default::default() + }) + .unwrap(); + + let static_files = provider_factory.static_file_provider(); + let writer = static_files.latest_writer(StaticFileSegment::Headers).unwrap(); + let actual_next_height = writer.next_block_number(); + let expected_next_height = 1701; + + assert_eq!(actual_next_height, expected_next_height); + } +} diff --git a/crates/cli/commands/src/stage/drop.rs b/crates/cli/commands/src/stage/drop.rs index 66227e10271..5a01ad1fed6 100644 --- a/crates/cli/commands/src/stage/drop.rs +++ b/crates/cli/commands/src/stage/drop.rs @@ -15,7 +15,7 @@ use reth_db_common::{ }; use reth_node_api::{HeaderTy, ReceiptTy, TxTy}; use reth_node_core::args::StageEnum; -use reth_provider::{DBProvider, DatabaseProviderFactory, StaticFileProviderFactory}; +use reth_provider::{DBProvider, DatabaseProviderFactory, StaticFileProviderFactory, TrieWriter}; use reth_prune::PruneSegment; use reth_stages::StageId; use reth_static_file_types::StaticFileSegment; @@ -138,6 +138,10 @@ impl Command { None, )?; } + StageEnum::MerkleChangeSets => { + provider_rw.clear_trie_changesets()?; + reset_stage_checkpoint(tx, StageId::MerkleChangeSets)?; + } StageEnum::AccountHistory | StageEnum::StorageHistory => { tx.clear::()?; tx.clear::()?; diff --git a/crates/cli/commands/src/stage/unwind.rs b/crates/cli/commands/src/stage/unwind.rs index 9ef2085a065..ba9a00b11e2 100644 --- a/crates/cli/commands/src/stage/unwind.rs +++ b/crates/cli/commands/src/stage/unwind.rs @@ -15,10 +15,7 @@ use reth_db::DatabaseEnv; use reth_downloaders::{bodies::noop::NoopBodiesDownloader, headers::noop::NoopHeaderDownloader}; use reth_evm::ConfigureEvm; use reth_exex::ExExManagerHandle; -use reth_provider::{ - providers::ProviderNodeTypes, BlockExecutionWriter, BlockNumReader, ChainStateBlockReader, - ChainStateBlockWriter, ProviderFactory, StaticFileProviderFactory, -}; +use reth_provider::{providers::ProviderNodeTypes, BlockNumReader, ProviderFactory}; use reth_stages::{ sets::{DefaultStages, OfflineStages}, stages::ExecutionStage, @@ -60,54 +57,21 @@ impl> Command let components = components(provider_factory.chain_spec()); - let highest_static_file_block = provider_factory - .static_file_provider() - .get_highest_static_files() - .max_block_num() - .filter(|highest_static_file_block| *highest_static_file_block > target); - - // Execute a pipeline unwind if the start of the range overlaps the existing static - // files. If that's the case, then copy all available data from MDBX to static files, and - // only then, proceed with the unwind. - // - // We also execute a pipeline unwind if `offline` is specified, because we need to only - // unwind the data associated with offline stages. - if highest_static_file_block.is_some() || self.offline { - if self.offline { - info!(target: "reth::cli", "Performing an unwind for offline-only data!"); - } - - if let Some(highest_static_file_block) = highest_static_file_block { - info!(target: "reth::cli", ?target, ?highest_static_file_block, "Executing a pipeline unwind."); - } else { - info!(target: "reth::cli", ?target, "Executing a pipeline unwind."); - } - info!(target: "reth::cli", prune_config=?config.prune, "Using prune settings"); - - // This will build an offline-only pipeline if the `offline` flag is enabled - let mut pipeline = - self.build_pipeline(config, provider_factory, components.evm_config().clone())?; - - // Move all applicable data from database to static files. - pipeline.move_to_static_files()?; + if self.offline { + info!(target: "reth::cli", "Performing an unwind for offline-only data!"); + } - pipeline.unwind(target, None)?; - } else { - info!(target: "reth::cli", ?target, "Executing a database unwind."); - let provider = provider_factory.provider_rw()?; + let highest_static_file_block = provider_factory.provider()?.last_block_number()?; + info!(target: "reth::cli", ?target, ?highest_static_file_block, prune_config=?config.prune, "Executing a pipeline unwind."); - provider - .remove_block_and_execution_above(target) - .map_err(|err| eyre::eyre!("Transaction error on unwind: {err}"))?; + // This will build an offline-only pipeline if the `offline` flag is enabled + let mut pipeline = + self.build_pipeline(config, provider_factory, components.evm_config().clone())?; - // update finalized block if needed - let last_saved_finalized_block_number = provider.last_finalized_block_number()?; - if last_saved_finalized_block_number.is_none_or(|f| f > target) { - provider.save_finalized_block_number(target)?; - } + // Move all applicable data from database to static files. + pipeline.move_to_static_files()?; - provider.commit()?; - } + pipeline.unwind(target, None)?; info!(target: "reth::cli", ?target, "Unwound blocks"); diff --git a/crates/cli/commands/src/test_vectors/compact.rs b/crates/cli/commands/src/test_vectors/compact.rs index ca88c131ff6..f4636f5f83b 100644 --- a/crates/cli/commands/src/test_vectors/compact.rs +++ b/crates/cli/commands/src/test_vectors/compact.rs @@ -283,7 +283,7 @@ pub fn type_name() -> String { // With alloy type transition the types are renamed, we map them here to the original name so that test vector files remain consistent let name = std::any::type_name::(); match name { - "alloy_consensus::transaction::typed::EthereumTypedTransaction" => "Transaction".to_string(), + "alloy_consensus::transaction::envelope::EthereumTypedTransaction" => "Transaction".to_string(), "alloy_consensus::transaction::envelope::EthereumTxEnvelope" => "TransactionSigned".to_string(), name => { name.split("::").last().unwrap_or(std::any::type_name::()).to_string() diff --git a/crates/cli/runner/src/lib.rs b/crates/cli/runner/src/lib.rs index 4f8e13ce8cb..79dc6b21142 100644 --- a/crates/cli/runner/src/lib.rs +++ b/crates/cli/runner/src/lib.rs @@ -36,11 +36,15 @@ impl CliRunner { pub const fn from_runtime(tokio_runtime: tokio::runtime::Runtime) -> Self { Self { tokio_runtime } } -} -// === impl CliRunner === + /// Executes an async block on the runtime and blocks until completion. + pub fn block_on(&self, fut: F) -> T + where + F: Future, + { + self.tokio_runtime.block_on(fut) + } -impl CliRunner { /// Executes the given _async_ command on the tokio runtime until the command future resolves or /// until the process receives a `SIGINT` or `SIGTERM` signal. /// diff --git a/crates/config/src/config.rs b/crates/config/src/config.rs index c1c5ef96075..7ea5569834c 100644 --- a/crates/config/src/config.rs +++ b/crates/config/src/config.rs @@ -440,7 +440,7 @@ pub struct PruneConfig { impl Default for PruneConfig { fn default() -> Self { - Self { block_interval: DEFAULT_BLOCK_INTERVAL, segments: PruneModes::none() } + Self { block_interval: DEFAULT_BLOCK_INTERVAL, segments: PruneModes::default() } } } @@ -464,6 +464,7 @@ impl PruneConfig { account_history, storage_history, bodies_history, + merkle_changesets, receipts_log_filter, }, } = other; @@ -480,6 +481,8 @@ impl PruneConfig { self.segments.account_history = self.segments.account_history.or(account_history); self.segments.storage_history = self.segments.storage_history.or(storage_history); self.segments.bodies_history = self.segments.bodies_history.or(bodies_history); + // Merkle changesets is not optional, so we just replace it if provided + self.segments.merkle_changesets = merkle_changesets; if self.segments.receipts_log_filter.0.is_empty() && !receipts_log_filter.0.is_empty() { self.segments.receipts_log_filter = receipts_log_filter; @@ -1001,6 +1004,7 @@ receipts = 'full' account_history: None, storage_history: Some(PruneMode::Before(5000)), bodies_history: None, + merkle_changesets: PruneMode::Before(0), receipts_log_filter: ReceiptsLogPruneConfig(BTreeMap::from([( Address::random(), PruneMode::Full, @@ -1017,6 +1021,7 @@ receipts = 'full' account_history: Some(PruneMode::Distance(2000)), storage_history: Some(PruneMode::Distance(3000)), bodies_history: None, + merkle_changesets: PruneMode::Distance(10000), receipts_log_filter: ReceiptsLogPruneConfig(BTreeMap::from([ (Address::random(), PruneMode::Distance(1000)), (Address::random(), PruneMode::Before(2000)), @@ -1035,6 +1040,7 @@ receipts = 'full' assert_eq!(config1.segments.receipts, Some(PruneMode::Distance(1000))); assert_eq!(config1.segments.account_history, Some(PruneMode::Distance(2000))); assert_eq!(config1.segments.storage_history, Some(PruneMode::Before(5000))); + assert_eq!(config1.segments.merkle_changesets, PruneMode::Distance(10000)); assert_eq!(config1.segments.receipts_log_filter, original_filter); } diff --git a/crates/e2e-test-utils/src/rpc.rs b/crates/e2e-test-utils/src/rpc.rs index 96dda811735..ff030c390b9 100644 --- a/crates/e2e-test-utils/src/rpc.rs +++ b/crates/e2e-test-utils/src/rpc.rs @@ -1,4 +1,5 @@ -use alloy_consensus::TxEnvelope; +use alloy_consensus::{EthereumTxEnvelope, TxEip4844Variant}; +use alloy_eips::eip7594::BlobTransactionSidecarVariant; use alloy_network::eip2718::Decodable2718; use alloy_primitives::{Bytes, B256}; use reth_chainspec::EthereumHardforks; @@ -30,9 +31,12 @@ where } /// Retrieves a transaction envelope by its hash - pub async fn envelope_by_hash(&self, hash: B256) -> eyre::Result { + pub async fn envelope_by_hash( + &self, + hash: B256, + ) -> eyre::Result>> { let tx = self.inner.debug_api().raw_transaction(hash).await?.unwrap(); let tx = tx.to_vec(); - Ok(TxEnvelope::decode_2718(&mut tx.as_ref()).unwrap()) + Ok(EthereumTxEnvelope::decode_2718(&mut tx.as_ref()).unwrap()) } } diff --git a/crates/e2e-test-utils/src/transaction.rs b/crates/e2e-test-utils/src/transaction.rs index 54f98469242..dd49ac76195 100644 --- a/crates/e2e-test-utils/src/transaction.rs +++ b/crates/e2e-test-utils/src/transaction.rs @@ -1,5 +1,7 @@ -use alloy_consensus::{EnvKzgSettings, SidecarBuilder, SimpleCoder, TxEip4844Variant, TxEnvelope}; -use alloy_eips::eip7702::SignedAuthorization; +use alloy_consensus::{ + EnvKzgSettings, EthereumTxEnvelope, SidecarBuilder, SimpleCoder, TxEip4844Variant, TxEnvelope, +}; +use alloy_eips::{eip7594::BlobTransactionSidecarVariant, eip7702::SignedAuthorization}; use alloy_network::{ eip2718::Encodable2718, Ethereum, EthereumWallet, TransactionBuilder, TransactionBuilder4844, }; @@ -146,11 +148,13 @@ impl TransactionTestContext { /// Validates the sidecar of a given tx envelope and returns the versioned hashes #[track_caller] - pub fn validate_sidecar(tx: TxEnvelope) -> Vec { + pub fn validate_sidecar( + tx: EthereumTxEnvelope>, + ) -> Vec { let proof_setting = EnvKzgSettings::Default; match tx { - TxEnvelope::Eip4844(signed) => match signed.tx() { + EthereumTxEnvelope::Eip4844(signed) => match signed.tx() { TxEip4844Variant::TxEip4844WithSidecar(tx) => { tx.validate_blob(proof_setting.get()).unwrap(); tx.sidecar.versioned_hashes().collect() diff --git a/crates/engine/invalid-block-hooks/src/witness.rs b/crates/engine/invalid-block-hooks/src/witness.rs index 1df76d9255c..d00f3b8287b 100644 --- a/crates/engine/invalid-block-hooks/src/witness.rs +++ b/crates/engine/invalid-block-hooks/src/witness.rs @@ -839,6 +839,7 @@ mod tests { receipts: vec![], requests: Requests::default(), gas_used: 0, + blob_gas_used: 0, }, }; diff --git a/crates/engine/primitives/src/config.rs b/crates/engine/primitives/src/config.rs index 9e2c8210f08..6f759036eb2 100644 --- a/crates/engine/primitives/src/config.rs +++ b/crates/engine/primitives/src/config.rs @@ -92,8 +92,8 @@ pub struct TreeConfig { /// Whether to always compare trie updates from the state root task to the trie updates from /// the regular state root calculation. always_compare_trie_updates: bool, - /// Whether to disable cross-block caching and parallel prewarming. - disable_caching_and_prewarming: bool, + /// Whether to disable parallel prewarming. + disable_prewarming: bool, /// Whether to disable the parallel sparse trie state root algorithm. disable_parallel_sparse_trie: bool, /// Whether to enable state provider metrics. @@ -148,7 +148,7 @@ impl Default for TreeConfig { max_execute_block_batch_size: DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE, legacy_state_root: false, always_compare_trie_updates: false, - disable_caching_and_prewarming: false, + disable_prewarming: false, disable_parallel_sparse_trie: false, state_provider_metrics: false, cross_block_cache_size: DEFAULT_CROSS_BLOCK_CACHE_SIZE, @@ -179,7 +179,7 @@ impl TreeConfig { max_execute_block_batch_size: usize, legacy_state_root: bool, always_compare_trie_updates: bool, - disable_caching_and_prewarming: bool, + disable_prewarming: bool, disable_parallel_sparse_trie: bool, state_provider_metrics: bool, cross_block_cache_size: u64, @@ -205,7 +205,7 @@ impl TreeConfig { max_execute_block_batch_size, legacy_state_root, always_compare_trie_updates, - disable_caching_and_prewarming, + disable_prewarming, disable_parallel_sparse_trie, state_provider_metrics, cross_block_cache_size, @@ -285,9 +285,9 @@ impl TreeConfig { self.disable_parallel_sparse_trie } - /// Returns whether or not cross-block caching and parallel prewarming should be used. - pub const fn disable_caching_and_prewarming(&self) -> bool { - self.disable_caching_and_prewarming + /// Returns whether or not parallel prewarming should be used. + pub const fn disable_prewarming(&self) -> bool { + self.disable_prewarming } /// Returns whether to always compare trie updates from the state root task to the trie updates @@ -377,12 +377,9 @@ impl TreeConfig { self } - /// Setter for whether to disable cross-block caching and parallel prewarming. - pub const fn without_caching_and_prewarming( - mut self, - disable_caching_and_prewarming: bool, - ) -> Self { - self.disable_caching_and_prewarming = disable_caching_and_prewarming; + /// Setter for whether to disable parallel prewarming. + pub const fn without_prewarming(mut self, disable_prewarming: bool) -> Self { + self.disable_prewarming = disable_prewarming; self } diff --git a/crates/engine/primitives/src/event.rs b/crates/engine/primitives/src/event.rs index 1c74282cba5..8cced031524 100644 --- a/crates/engine/primitives/src/event.rs +++ b/crates/engine/primitives/src/event.rs @@ -10,7 +10,7 @@ use core::{ fmt::{Display, Formatter, Result}, time::Duration, }; -use reth_chain_state::ExecutedBlockWithTrieUpdates; +use reth_chain_state::ExecutedBlock; use reth_ethereum_primitives::EthPrimitives; use reth_primitives_traits::{NodePrimitives, SealedBlock, SealedHeader}; @@ -24,11 +24,11 @@ pub enum ConsensusEngineEvent { /// The fork choice state was updated, and the current fork choice status ForkchoiceUpdated(ForkchoiceState, ForkchoiceStatus), /// A block was added to the fork chain. - ForkBlockAdded(ExecutedBlockWithTrieUpdates, Duration), + ForkBlockAdded(ExecutedBlock, Duration), /// A new block was received from the consensus engine BlockReceived(BlockNumHash), /// A block was added to the canonical chain, and the elapsed time validating the block - CanonicalBlockAdded(ExecutedBlockWithTrieUpdates, Duration), + CanonicalBlockAdded(ExecutedBlock, Duration), /// A canonical chain was committed, and the elapsed time committing the data CanonicalChainCommitted(Box>, Duration), /// The consensus engine processed an invalid block. diff --git a/crates/engine/tree/src/chain.rs b/crates/engine/tree/src/chain.rs index e2893bb976a..d1e63a6b3d9 100644 --- a/crates/engine/tree/src/chain.rs +++ b/crates/engine/tree/src/chain.rs @@ -71,7 +71,7 @@ where /// Internal function used to advance the chain. /// /// Polls the `ChainOrchestrator` for the next event. - #[tracing::instrument(level = "debug", name = "ChainOrchestrator::poll", skip(self, cx))] + #[tracing::instrument(name = "ChainOrchestrator::poll", skip(self, cx))] fn poll_next_event(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll> { let this = self.get_mut(); diff --git a/crates/engine/tree/src/engine.rs b/crates/engine/tree/src/engine.rs index bee52a46438..f08195b205e 100644 --- a/crates/engine/tree/src/engine.rs +++ b/crates/engine/tree/src/engine.rs @@ -7,7 +7,7 @@ use crate::{ }; use alloy_primitives::B256; use futures::{Stream, StreamExt}; -use reth_chain_state::ExecutedBlockWithTrieUpdates; +use reth_chain_state::ExecutedBlock; use reth_engine_primitives::{BeaconEngineMessage, ConsensusEngineEvent}; use reth_ethereum_primitives::EthPrimitives; use reth_payload_primitives::PayloadTypes; @@ -246,7 +246,7 @@ pub enum EngineApiRequest { /// A request received from the consensus engine. Beacon(BeaconEngineMessage), /// Request to insert an already executed block, e.g. via payload building. - InsertExecutedBlock(ExecutedBlockWithTrieUpdates), + InsertExecutedBlock(ExecutedBlock), } impl Display for EngineApiRequest { diff --git a/crates/engine/tree/src/persistence.rs b/crates/engine/tree/src/persistence.rs index de5b10c331c..751356fc399 100644 --- a/crates/engine/tree/src/persistence.rs +++ b/crates/engine/tree/src/persistence.rs @@ -1,7 +1,7 @@ use crate::metrics::PersistenceMetrics; use alloy_consensus::BlockHeader; use alloy_eips::BlockNumHash; -use reth_chain_state::ExecutedBlockWithTrieUpdates; +use reth_chain_state::ExecutedBlock; use reth_errors::ProviderError; use reth_ethereum_primitives::EthPrimitives; use reth_primitives_traits::NodePrimitives; @@ -140,7 +140,7 @@ where fn on_save_blocks( &self, - blocks: Vec>, + blocks: Vec>, ) -> Result, PersistenceError> { debug!(target: "engine::persistence", first=?blocks.first().map(|b| b.recovered_block.num_hash()), last=?blocks.last().map(|b| b.recovered_block.num_hash()), "Saving range of blocks"); let start_time = Instant::now(); @@ -180,7 +180,7 @@ pub enum PersistenceAction { /// /// First, header, transaction, and receipt-related data should be written to static files. /// Then the execution history-related data will be written to the database. - SaveBlocks(Vec>, oneshot::Sender>), + SaveBlocks(Vec>, oneshot::Sender>), /// Removes block data above the given block number from the database. /// @@ -257,7 +257,7 @@ impl PersistenceHandle { /// If there are no blocks to persist, then `None` is sent in the sender. pub fn save_blocks( &self, - blocks: Vec>, + blocks: Vec>, tx: oneshot::Sender>, ) -> Result<(), SendError>> { self.send_action(PersistenceAction::SaveBlocks(blocks, tx)) diff --git a/crates/engine/tree/src/test_utils.rs b/crates/engine/tree/src/test_utils.rs index 2ec00f9b918..e011a54b73c 100644 --- a/crates/engine/tree/src/test_utils.rs +++ b/crates/engine/tree/src/test_utils.rs @@ -3,9 +3,8 @@ use reth_chainspec::ChainSpec; use reth_ethereum_primitives::BlockBody; use reth_network_p2p::test_utils::TestFullBlockClient; use reth_primitives_traits::SealedHeader; -use reth_provider::{ - test_utils::{create_test_provider_factory_with_chain_spec, MockNodeTypesWithDB}, - ExecutionOutcome, +use reth_provider::test_utils::{ + create_test_provider_factory_with_chain_spec, MockNodeTypesWithDB, }; use reth_prune_types::PruneModes; use reth_stages::{test_utils::TestStages, ExecOutput, StageError}; @@ -18,13 +17,12 @@ use tokio::sync::watch; #[derive(Default, Debug)] pub struct TestPipelineBuilder { pipeline_exec_outputs: VecDeque>, - executor_results: Vec, } impl TestPipelineBuilder { /// Create a new [`TestPipelineBuilder`]. pub const fn new() -> Self { - Self { pipeline_exec_outputs: VecDeque::new(), executor_results: Vec::new() } + Self { pipeline_exec_outputs: VecDeque::new() } } /// Set the pipeline execution outputs to use for the test consensus engine. @@ -37,8 +35,14 @@ impl TestPipelineBuilder { } /// Set the executor results to use for the test consensus engine. - pub fn with_executor_results(mut self, executor_results: Vec) -> Self { - self.executor_results = executor_results; + #[deprecated( + note = "no-op: executor results are not used and will be removed in a future release" + )] + pub fn with_executor_results( + self, + executor_results: Vec, + ) -> Self { + let _ = executor_results; self } diff --git a/crates/engine/tree/src/tree/cached_state.rs b/crates/engine/tree/src/tree/cached_state.rs index 8553a9fe63c..3e9cda38f13 100644 --- a/crates/engine/tree/src/tree/cached_state.rs +++ b/crates/engine/tree/src/tree/cached_state.rs @@ -18,7 +18,7 @@ use reth_trie::{ MultiProofTargets, StorageMultiProof, StorageProof, TrieInput, }; use std::{sync::Arc, time::Duration}; -use tracing::trace; +use tracing::{debug_span, instrument, trace}; pub(crate) type Cache = mini_moka::sync::Cache; @@ -354,6 +354,7 @@ impl ExecutionCache { } /// Invalidates the storage for all addresses in the set + #[instrument(level = "debug", target = "engine::tree", skip_all, fields(accounts = addresses.len()))] pub(crate) fn invalidate_storages(&self, addresses: HashSet<&Address>) { // NOTE: this must collect because the invalidate function should not be called while we // hold an iter for it @@ -385,12 +386,25 @@ impl ExecutionCache { /// ## Error Handling /// /// Returns an error if the state updates are inconsistent and should be discarded. + #[instrument(level = "debug", target = "engine::tree", skip_all)] pub(crate) fn insert_state(&self, state_updates: &BundleState) -> Result<(), ()> { + let _enter = + debug_span!(target: "engine::tree", "contracts", len = state_updates.contracts.len()) + .entered(); // Insert bytecodes for (code_hash, bytecode) in &state_updates.contracts { self.code_cache.insert(*code_hash, Some(Bytecode(bytecode.clone()))); } - + drop(_enter); + + let _enter = debug_span!( + target: "engine::tree", + "accounts", + accounts = state_updates.state.len(), + storages = + state_updates.state.values().map(|account| account.storage.len()).sum::() + ) + .entered(); let mut invalidated_accounts = HashSet::default(); for (addr, account) in &state_updates.state { // If the account was not modified, as in not changed and not destroyed, then we have @@ -474,9 +488,9 @@ impl ExecutionCacheBuilder { .build_with_hasher(DefaultHashBuilder::default()); let account_cache = CacheBuilder::new(self.account_cache_entries) - .weigher(|_key: &Address, _value: &Option| -> u32 { + .weigher(|_key: &Address, value: &Option| -> u32 { // Account has a fixed size (none, balance,code_hash) - size_of::>() as u32 + 20 + size_of_val(value) as u32 }) .max_capacity(account_cache_size) .time_to_live(EXPIRY_TIME) @@ -485,13 +499,19 @@ impl ExecutionCacheBuilder { let code_cache = CacheBuilder::new(self.code_cache_entries) .weigher(|_key: &B256, value: &Option| -> u32 { - match value { + let code_size = match value { Some(bytecode) => { - // base weight + actual bytecode size - (40 + bytecode.len()) as u32 + // base weight + actual (padded) bytecode size + size of the jump table + (size_of_val(value) + + bytecode.bytecode().len() + + bytecode + .legacy_jump_table() + .map(|table| table.as_slice().len()) + .unwrap_or_default()) as u32 } - None => 8, // size of None variant - } + None => size_of_val(value) as u32, + }; + 32 + code_size }) .max_capacity(code_cache_size) .time_to_live(EXPIRY_TIME) diff --git a/crates/engine/tree/src/tree/error.rs b/crates/engine/tree/src/tree/error.rs index f7b1111df06..8589bc59d3d 100644 --- a/crates/engine/tree/src/tree/error.rs +++ b/crates/engine/tree/src/tree/error.rs @@ -1,7 +1,6 @@ //! Internal errors for the tree module. use alloy_consensus::BlockHeader; -use alloy_primitives::B256; use reth_consensus::ConsensusError; use reth_errors::{BlockExecutionError, BlockValidationError, ProviderError}; use reth_evm::execute::InternalBlockExecutionError; @@ -19,20 +18,6 @@ pub enum AdvancePersistenceError { /// A provider error #[error(transparent)] Provider(#[from] ProviderError), - /// Missing ancestor. - /// - /// This error occurs when we need to compute the state root for a block with missing trie - /// updates, but the ancestor block is not available. State root computation requires the state - /// from the parent block as a starting point. - /// - /// A block may be missing the trie updates when it's a fork chain block building on top of the - /// historical database state. Since we don't store the historical trie state, we cannot - /// generate the trie updates for it until the moment when database is unwound to the canonical - /// chain. - /// - /// Also see [`reth_chain_state::ExecutedTrieUpdates::Missing`]. - #[error("Missing ancestor with hash {0}")] - MissingAncestor(B256), } #[derive(thiserror::Error)] diff --git a/crates/engine/tree/src/tree/metrics.rs b/crates/engine/tree/src/tree/metrics.rs index 844db1e63b9..1d1e208b0a6 100644 --- a/crates/engine/tree/src/tree/metrics.rs +++ b/crates/engine/tree/src/tree/metrics.rs @@ -79,7 +79,7 @@ impl EngineApiMetrics { for tx in transactions { let tx = tx?; let span = - debug_span!(target: "engine::tree", "execute_tx", tx_hash=?tx.tx().tx_hash()); + debug_span!(target: "engine::tree", "execute tx", tx_hash=?tx.tx().tx_hash()); let _enter = span.enter(); trace!(target: "engine::tree", "Executing transaction"); executor.execute_transaction(tx)?; @@ -314,6 +314,7 @@ mod tests { receipts: vec![], requests: Requests::default(), gas_used: 1000, + blob_gas_used: 0, }, )) } diff --git a/crates/engine/tree/src/tree/mod.rs b/crates/engine/tree/src/tree/mod.rs index 7f1183f5efc..a189b643f98 100644 --- a/crates/engine/tree/src/tree/mod.rs +++ b/crates/engine/tree/src/tree/mod.rs @@ -13,10 +13,8 @@ use alloy_rpc_types_engine::{ ForkchoiceState, PayloadStatus, PayloadStatusEnum, PayloadValidationError, }; use error::{InsertBlockError, InsertBlockFatalError}; -use persistence_state::CurrentPersistenceAction; use reth_chain_state::{ - CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates, - MemoryOverlayStateProvider, NewCanonicalChain, + CanonicalInMemoryState, ExecutedBlock, MemoryOverlayStateProvider, NewCanonicalChain, }; use reth_consensus::{Consensus, FullConsensus}; use reth_engine_primitives::{ @@ -31,14 +29,12 @@ use reth_payload_primitives::{ }; use reth_primitives_traits::{NodePrimitives, RecoveredBlock, SealedBlock, SealedHeader}; use reth_provider::{ - providers::ConsistentDbView, BlockNumReader, BlockReader, DBProvider, DatabaseProviderFactory, - HashedPostStateProvider, ProviderError, StateProviderBox, StateProviderFactory, StateReader, - StateRootProvider, TransactionVariant, + providers::ConsistentDbView, BlockReader, DatabaseProviderFactory, HashedPostStateProvider, + ProviderError, StateProviderBox, StateProviderFactory, StateReader, TransactionVariant, + TrieReader, }; use reth_revm::database::StateProviderDatabase; use reth_stages_api::ControlFlow; -use reth_trie::{HashedPostState, TrieInput}; -use reth_trie_db::DatabaseHashedPostState; use revm::state::EvmState; use state::TreeState; use std::{ @@ -78,7 +74,6 @@ pub use payload_processor::*; pub use payload_validator::{BasicEngineValidator, EngineValidator}; pub use persistence_state::PersistenceState; pub use reth_engine_primitives::TreeConfig; -use reth_trie::KeccakKeyHasher; pub mod state; @@ -101,7 +96,7 @@ pub struct StateProviderBuilder { /// The historical block hash to fetch state from. historical: B256, /// The blocks that form the chain from historical to target and are in memory. - overlay: Option>>, + overlay: Option>>, } impl StateProviderBuilder { @@ -110,7 +105,7 @@ impl StateProviderBuilder { pub const fn new( provider_factory: P, historical: B256, - overlay: Option>>, + overlay: Option>>, ) -> Self { Self { provider_factory, historical, overlay } } @@ -318,6 +313,7 @@ where + StateProviderFactory + StateReader + HashedPostStateProvider + + TrieReader + Clone + 'static,

::Provider: @@ -500,7 +496,12 @@ where /// /// This returns a [`PayloadStatus`] that represents the outcome of a processed new payload and /// returns an error if an internal error occurred. - #[instrument(level = "trace", skip_all, fields(block_hash = %payload.block_hash(), block_num = %payload.block_number(),), target = "engine::tree")] + #[instrument( + level = "debug", + target = "engine::tree", + skip_all, + fields(block_hash = %payload.block_hash(), block_num = %payload.block_number()), + )] fn on_new_payload( &mut self, payload: T::ExecutionData, @@ -581,6 +582,7 @@ where /// - `Valid`: Payload successfully validated and inserted /// - `Syncing`: Parent missing, payload buffered for later /// - Error status: Payload is invalid + #[instrument(level = "debug", target = "engine::tree", skip_all)] fn try_insert_payload( &mut self, payload: T::ExecutionData, @@ -823,7 +825,7 @@ where for block_num in (new_head_number + 1)..=current_head_number { if let Some(block_state) = self.canonical_in_memory_state.state_by_number(block_num) { - let executed_block = block_state.block_ref().block.clone(); + let executed_block = block_state.block_ref().clone(); old_blocks.push(executed_block); debug!( target: "engine::tree", @@ -855,14 +857,9 @@ where // Try to load the canonical ancestor's block match self.canonical_block_by_hash(new_head_hash)? { Some(executed_block) => { - let block_with_trie = ExecutedBlockWithTrieUpdates { - block: executed_block, - trie: ExecutedTrieUpdates::Missing, - }; - // Perform the reorg to properly handle the unwind self.canonical_in_memory_state.update_chain(NewCanonicalChain::Reorg { - new: vec![block_with_trie], + new: vec![executed_block], old: old_blocks, }); @@ -915,13 +912,8 @@ where // Try to load the block from storage if let Some(executed_block) = self.canonical_block_by_hash(block_hash)? { - let block_with_trie = ExecutedBlockWithTrieUpdates { - block: executed_block, - trie: ExecutedTrieUpdates::Missing, - }; - self.canonical_in_memory_state - .update_chain(NewCanonicalChain::Commit { new: vec![block_with_trie] }); + .update_chain(NewCanonicalChain::Commit { new: vec![executed_block] }); debug!( target: "engine::tree", @@ -976,29 +968,6 @@ where Ok(true) } - /// Returns the persisting kind for the input block. - fn persisting_kind_for(&self, block: BlockWithParent) -> PersistingKind { - // Check that we're currently persisting. - let Some(action) = self.persistence_state.current_action() else { - return PersistingKind::NotPersisting - }; - // Check that the persistince action is saving blocks, not removing them. - let CurrentPersistenceAction::SavingBlocks { highest } = action else { - return PersistingKind::PersistingNotDescendant - }; - - // The block being validated can only be a descendant if its number is higher than - // the highest block persisting. Otherwise, it's likely a fork of a lower block. - if block.block.number > highest.number && - self.state.tree_state.is_descendant(*highest, block) - { - return PersistingKind::PersistingDescendant - } - - // In all other cases, the block is not a descendant. - PersistingKind::PersistingNotDescendant - } - /// Invoked when we receive a new forkchoice update message. Calls into the blockchain tree /// to resolve chain forks and ensure that the Execution Layer is working with the latest valid /// chain. @@ -1007,7 +976,7 @@ where /// `engine_forkchoiceUpdated`](https://github.com/ethereum/execution-apis/blob/main/src/engine/paris.md#specification-1). /// /// Returns an error if an internal error occurred like a database error. - #[instrument(level = "trace", skip_all, fields(head = % state.head_block_hash, safe = % state.safe_block_hash,finalized = % state.finalized_block_hash), target = "engine::tree")] + #[instrument(level = "debug", target = "engine::tree", skip_all, fields(head = % state.head_block_hash, safe = % state.safe_block_hash,finalized = % state.finalized_block_hash))] fn on_forkchoice_updated( &mut self, state: ForkchoiceState, @@ -1305,7 +1274,7 @@ where /// Helper method to save blocks and set the persistence state. This ensures we keep track of /// the current persistence action while we're saving blocks. - fn persist_blocks(&mut self, blocks_to_persist: Vec>) { + fn persist_blocks(&mut self, blocks_to_persist: Vec>) { if blocks_to_persist.is_empty() { debug!(target: "engine::tree", "Returned empty set of blocks to persist"); return @@ -1696,17 +1665,9 @@ where /// Returns a batch of consecutive canonical blocks to persist in the range /// `(last_persisted_number .. canonical_head - threshold]`. The expected /// order is oldest -> newest. - /// - /// If any blocks are missing trie updates, all blocks are persisted, not taking `threshold` - /// into account. - /// - /// For those blocks that didn't have the trie updates calculated, runs the state root - /// calculation, and saves the trie updates. - /// - /// Returns an error if the state root calculation fails. fn get_canonical_blocks_to_persist( - &mut self, - ) -> Result>, AdvancePersistenceError> { + &self, + ) -> Result>, AdvancePersistenceError> { // We will calculate the state root using the database, so we need to be sure there are no // changes debug_assert!(!self.persistence_state.in_progress()); @@ -1715,27 +1676,16 @@ where let mut current_hash = self.state.tree_state.canonical_block_hash(); let last_persisted_number = self.persistence_state.last_persisted_block.number; let canonical_head_number = self.state.tree_state.canonical_block_number(); - let all_blocks_have_trie_updates = self - .state - .tree_state - .blocks_by_hash - .values() - .all(|block| block.trie_updates().is_some()); - - let target_number = if all_blocks_have_trie_updates { - // Persist only up to block buffer target if all blocks have trie updates - canonical_head_number.saturating_sub(self.config.memory_block_buffer_target()) - } else { - // Persist all blocks if any block is missing trie updates - canonical_head_number - }; + + // Persist only up to block buffer target + let target_number = + canonical_head_number.saturating_sub(self.config.memory_block_buffer_target()); debug!( target: "engine::tree", ?current_hash, ?last_persisted_number, ?canonical_head_number, - ?all_blocks_have_trie_updates, ?target_number, "Returning canonical blocks to persist" ); @@ -1754,48 +1704,6 @@ where // Reverse the order so that the oldest block comes first blocks_to_persist.reverse(); - // Calculate missing trie updates - for block in &mut blocks_to_persist { - if block.trie.is_present() { - continue - } - - debug!( - target: "engine::tree", - block = ?block.recovered_block().num_hash(), - "Calculating trie updates before persisting" - ); - - let provider = self - .state_provider_builder(block.recovered_block().parent_hash())? - .ok_or(AdvancePersistenceError::MissingAncestor( - block.recovered_block().parent_hash(), - ))? - .build()?; - - let mut trie_input = self.compute_trie_input( - self.persisting_kind_for(block.recovered_block.block_with_parent()), - self.provider.database_provider_ro()?, - block.recovered_block().parent_hash(), - None, - )?; - // Extend with block we are generating trie updates for. - trie_input.append_ref(block.hashed_state()); - let (_root, updates) = provider.state_root_from_nodes_with_updates(trie_input)?; - debug_assert_eq!(_root, block.recovered_block().state_root()); - - // Update trie updates in both tree state and blocks to persist that we return - let trie_updates = Arc::new(updates); - let tree_state_block = self - .state - .tree_state - .blocks_by_hash - .get_mut(&block.recovered_block().hash()) - .expect("blocks to persist are constructed from tree state blocks"); - tree_state_block.trie.set_present(trie_updates.clone()); - block.trie.set_present(trie_updates); - } - Ok(blocks_to_persist) } @@ -1834,7 +1742,7 @@ where trace!(target: "engine::tree", ?hash, "Fetching executed block by hash"); // check memory first if let Some(block) = self.state.tree_state.executed_block_by_hash(hash) { - return Ok(Some(block.block.clone())) + return Ok(Some(block.clone())) } let (block, senders) = self @@ -1847,11 +1755,13 @@ where .get_state(block.header().number())? .ok_or_else(|| ProviderError::StateForNumberNotFound(block.header().number()))?; let hashed_state = self.provider.hashed_post_state(execution_output.state()); + let trie_updates = self.provider.get_block_trie_updates(block.number())?; Ok(Some(ExecutedBlock { recovered_block: Arc::new(RecoveredBlock::new_sealed(block, senders)), execution_output: Arc::new(execution_output), hashed_state: Arc::new(hashed_state), + trie_updates: Arc::new(trie_updates.into()), })) } @@ -2068,7 +1978,7 @@ where } /// Attempts to connect any buffered blocks that are connected to the given parent hash. - #[instrument(level = "trace", skip(self), target = "engine::tree")] + #[instrument(level = "debug", target = "engine::tree", skip(self))] fn try_connect_buffered_blocks( &mut self, parent: BlockNumHash, @@ -2289,25 +2199,7 @@ where self.update_reorg_metrics(old.len()); self.reinsert_reorged_blocks(new.clone()); - // Try reinserting the reorged canonical chain. This is only possible if we have - // `persisted_trie_updates` for those blocks. - let old = old - .iter() - .filter_map(|block| { - let trie = self - .state - .tree_state - .persisted_trie_updates - .get(&block.recovered_block.hash())? - .1 - .clone(); - Some(ExecutedBlockWithTrieUpdates { - block: block.clone(), - trie: ExecutedTrieUpdates::Present(trie), - }) - }) - .collect::>(); - self.reinsert_reorged_blocks(old); + self.reinsert_reorged_blocks(old.clone()); } // update the tracked in-memory state with the new chain @@ -2334,7 +2226,7 @@ where } /// This reinserts any blocks in the new chain that do not already exist in the tree - fn reinsert_reorged_blocks(&mut self, new_chain: Vec>) { + fn reinsert_reorged_blocks(&mut self, new_chain: Vec>) { for block in new_chain { if self .state @@ -2395,7 +2287,7 @@ where /// Returns an event with the appropriate action to take, such as: /// - download more missing blocks /// - try to canonicalize the target if the `block` is the tracked target (head) block. - #[instrument(level = "trace", skip_all, fields(block_hash = %block.hash(), block_num = %block.number(),), target = "engine::tree")] + #[instrument(level = "debug", target = "engine::tree", skip_all, fields(block_hash = %block.hash(), block_num = %block.number()))] fn on_downloaded_block( &mut self, block: RecoveredBlock, @@ -2501,15 +2393,12 @@ where /// Returns `InsertPayloadOk::Inserted(BlockStatus::Valid)` on successful execution, /// `InsertPayloadOk::AlreadySeen` if the block already exists, or /// `InsertPayloadOk::Inserted(BlockStatus::Disconnected)` if parent state is missing. + #[instrument(level = "debug", target = "engine::tree", skip_all, fields(block_id))] fn insert_block_or_payload( &mut self, block_id: BlockWithParent, input: Input, - execute: impl FnOnce( - &mut V, - Input, - TreeCtx<'_, N>, - ) -> Result, Err>, + execute: impl FnOnce(&mut V, Input, TreeCtx<'_, N>) -> Result, Err>, convert_to_block: impl FnOnce(&mut Self, Input) -> Result, Err>, ) -> Result where @@ -2604,109 +2493,6 @@ where Ok(InsertPayloadOk::Inserted(BlockStatus::Valid)) } - /// Computes the trie input at the provided parent hash. - /// - /// The goal of this function is to take in-memory blocks and generate a [`TrieInput`] that - /// serves as an overlay to the database blocks. - /// - /// It works as follows: - /// 1. Collect in-memory blocks that are descendants of the provided parent hash using - /// [`TreeState::blocks_by_hash`]. - /// 2. If the persistence is in progress, and the block that we're computing the trie input for - /// is a descendant of the currently persisting blocks, we need to be sure that in-memory - /// blocks are not overlapping with the database blocks that may have been already persisted. - /// To do that, we're filtering out in-memory blocks that are lower than the highest database - /// block. - /// 3. Once in-memory blocks are collected and optionally filtered, we compute the - /// [`HashedPostState`] from them. - fn compute_trie_input( - &self, - persisting_kind: PersistingKind, - provider: TP, - parent_hash: B256, - allocated_trie_input: Option, - ) -> ProviderResult { - // get allocated trie input or use a default trie input - let mut input = allocated_trie_input.unwrap_or_default(); - - let best_block_number = provider.best_block_number()?; - - let (mut historical, mut blocks) = self - .state - .tree_state - .blocks_by_hash(parent_hash) - .map_or_else(|| (parent_hash.into(), vec![]), |(hash, blocks)| (hash.into(), blocks)); - - // If the current block is a descendant of the currently persisting blocks, then we need to - // filter in-memory blocks, so that none of them are already persisted in the database. - if persisting_kind.is_descendant() { - // Iterate over the blocks from oldest to newest. - while let Some(block) = blocks.last() { - let recovered_block = block.recovered_block(); - if recovered_block.number() <= best_block_number { - // Remove those blocks that lower than or equal to the highest database - // block. - blocks.pop(); - } else { - // If the block is higher than the best block number, stop filtering, as it's - // the first block that's not in the database. - break - } - } - - historical = if let Some(block) = blocks.last() { - // If there are any in-memory blocks left after filtering, set the anchor to the - // parent of the oldest block. - (block.recovered_block().number() - 1).into() - } else { - // Otherwise, set the anchor to the original provided parent hash. - parent_hash.into() - }; - } - - if blocks.is_empty() { - debug!(target: "engine::tree", %parent_hash, "Parent found on disk"); - } else { - debug!(target: "engine::tree", %parent_hash, %historical, blocks = blocks.len(), "Parent found in memory"); - } - - // Convert the historical block to the block number. - let block_number = provider - .convert_hash_or_number(historical)? - .ok_or_else(|| ProviderError::BlockHashNotFound(historical.as_hash().unwrap()))?; - - // Retrieve revert state for historical block. - let revert_state = if block_number == best_block_number { - // We do not check against the `last_block_number` here because - // `HashedPostState::from_reverts` only uses the database tables, and not static files. - debug!(target: "engine::tree", block_number, best_block_number, "Empty revert state"); - HashedPostState::default() - } else { - let revert_state = HashedPostState::from_reverts::( - provider.tx_ref(), - block_number + 1.., - ) - .map_err(ProviderError::from)?; - debug!( - target: "engine::tree", - block_number, - best_block_number, - accounts = revert_state.accounts.len(), - storages = revert_state.storages.len(), - "Non-empty revert state" - ); - revert_state - }; - input.append(revert_state); - - // Extend with contents of parent in-memory blocks. - input.extend_with_blocks( - blocks.iter().rev().map(|block| (block.hashed_state(), block.trie_updates())), - ); - - Ok(input) - } - /// Handles an error that occurred while inserting a block. /// /// If this is a validation error this will mark the block as invalid. diff --git a/crates/engine/tree/src/tree/payload_processor/configured_sparse_trie.rs b/crates/engine/tree/src/tree/payload_processor/configured_sparse_trie.rs index 176cffcd8fa..90e8928dba2 100644 --- a/crates/engine/tree/src/tree/payload_processor/configured_sparse_trie.rs +++ b/crates/engine/tree/src/tree/payload_processor/configured_sparse_trie.rs @@ -172,4 +172,18 @@ impl SparseTrieInterface for ConfiguredSparseTrie { Self::Parallel(trie) => trie.updates_ref(), } } + + fn node_capacity(&self) -> usize { + match self { + Self::Serial(trie) => trie.node_capacity(), + Self::Parallel(trie) => trie.node_capacity(), + } + } + + fn value_capacity(&self) -> usize { + match self { + Self::Serial(trie) => trie.value_capacity(), + Self::Parallel(trie) => trie.value_capacity(), + } + } } diff --git a/crates/engine/tree/src/tree/payload_processor/executor.rs b/crates/engine/tree/src/tree/payload_processor/executor.rs index 3013c5e1c72..28165d5e8f2 100644 --- a/crates/engine/tree/src/tree/payload_processor/executor.rs +++ b/crates/engine/tree/src/tree/payload_processor/executor.rs @@ -1,10 +1,6 @@ //! Executor for mixed I/O and CPU workloads. -use rayon::ThreadPool as RayonPool; -use std::{ - sync::{Arc, OnceLock}, - time::Duration, -}; +use std::{sync::OnceLock, time::Duration}; use tokio::{ runtime::{Builder, Handle, Runtime}, task::JoinHandle, @@ -12,9 +8,8 @@ use tokio::{ /// An executor for mixed I/O and CPU workloads. /// -/// This type has access to its own rayon pool and uses tokio to spawn blocking tasks. -/// -/// It will reuse an existing tokio runtime if available or create its own. +/// This type uses tokio to spawn blocking tasks and will reuse an existing tokio +/// runtime if available or create its own. #[derive(Debug, Clone)] pub struct WorkloadExecutor { inner: WorkloadExecutorInner, @@ -22,21 +17,11 @@ pub struct WorkloadExecutor { impl Default for WorkloadExecutor { fn default() -> Self { - Self { inner: WorkloadExecutorInner::new(rayon::ThreadPoolBuilder::new().build().unwrap()) } + Self { inner: WorkloadExecutorInner::new() } } } impl WorkloadExecutor { - /// Creates a new executor with the given number of threads for cpu bound work (rayon). - #[expect(unused)] - pub(super) fn with_num_cpu_threads(cpu_threads: usize) -> Self { - Self { - inner: WorkloadExecutorInner::new( - rayon::ThreadPoolBuilder::new().num_threads(cpu_threads).build().unwrap(), - ), - } - } - /// Returns the handle to the tokio runtime pub(super) const fn handle(&self) -> &Handle { &self.inner.handle @@ -51,22 +36,15 @@ impl WorkloadExecutor { { self.inner.handle.spawn_blocking(func) } - - /// Returns access to the rayon pool - #[expect(unused)] - pub(super) const fn rayon_pool(&self) -> &Arc { - &self.inner.rayon_pool - } } #[derive(Debug, Clone)] struct WorkloadExecutorInner { handle: Handle, - rayon_pool: Arc, } impl WorkloadExecutorInner { - fn new(rayon_pool: rayon::ThreadPool) -> Self { + fn new() -> Self { fn get_runtime_handle() -> Handle { Handle::try_current().unwrap_or_else(|_| { // Create a new runtime if no runtime is available @@ -90,6 +68,6 @@ impl WorkloadExecutorInner { }) } - Self { handle: get_runtime_handle(), rayon_pool: Arc::new(rayon_pool) } + Self { handle: get_runtime_handle() } } } diff --git a/crates/engine/tree/src/tree/payload_processor/mod.rs b/crates/engine/tree/src/tree/payload_processor/mod.rs index f3ecdfa86d5..8d6230dd82f 100644 --- a/crates/engine/tree/src/tree/payload_processor/mod.rs +++ b/crates/engine/tree/src/tree/payload_processor/mod.rs @@ -45,7 +45,7 @@ use std::sync::{ mpsc::{self, channel, Sender}, Arc, }; -use tracing::{debug, instrument, warn}; +use tracing::{debug, debug_span, instrument, warn}; mod configured_sparse_trie; pub mod executor; @@ -117,7 +117,7 @@ where execution_cache: Default::default(), trie_metrics: Default::default(), cross_block_cache_size: config.cross_block_cache_size(), - disable_transaction_prewarming: config.disable_caching_and_prewarming(), + disable_transaction_prewarming: config.disable_prewarming(), evm_config, precompile_cache_disabled: config.precompile_cache_disabled(), precompile_cache_map, @@ -166,9 +166,13 @@ where /// /// This returns a handle to await the final state root and to interact with the tasks (e.g. /// canceling) - /// - /// Returns an error with the original transactions iterator if proof worker spawning fails. #[allow(clippy::type_complexity)] + #[instrument( + level = "debug", + target = "engine::tree::payload_processor", + name = "payload processor", + skip_all + )] pub fn spawn>( &mut self, env: ExecutionEnv, @@ -179,7 +183,7 @@ where config: &TreeConfig, ) -> Result< PayloadHandle, I::Tx>, I::Error>, - (reth_provider::ProviderError, I, ExecutionEnv, StateProviderBuilder), + (ParallelStateRootError, I, ExecutionEnv, StateProviderBuilder), > where P: DatabaseProviderFactory @@ -203,18 +207,13 @@ where let storage_worker_count = config.storage_worker_count(); let account_worker_count = config.account_worker_count(); let max_proof_task_concurrency = config.max_proof_task_concurrency() as usize; - let proof_handle = match ProofWorkerHandle::new( + let proof_handle = ProofWorkerHandle::new( self.executor.handle().clone(), consistent_view, task_ctx, storage_worker_count, account_worker_count, - ) { - Ok(handle) => handle, - Err(error) => { - return Err((error, transactions, env, provider_builder)); - } - }; + ); // We set it to half of the proof task concurrency, because often for each multiproof we // spawn one Tokio task for the account proof, and one Tokio task for the storage proof. @@ -243,7 +242,9 @@ where ); // spawn multi-proof task + let span = tracing::Span::current(); self.executor.spawn_blocking(move || { + let _enter = span.entered(); multi_proof_task.run(); }); @@ -264,6 +265,7 @@ where /// Spawns a task that exclusively handles cache prewarming for transaction execution. /// /// Returns a [`PayloadHandle`] to communicate with the task. + #[instrument(level = "debug", target = "engine::tree::payload_processor", skip_all)] pub(super) fn spawn_cache_exclusive>( &self, env: ExecutionEnv, @@ -360,7 +362,9 @@ where // spawn pre-warm task { let to_prewarm_task = to_prewarm_task.clone(); + let span = debug_span!(target: "engine::tree::payload_processor", "prewarm task"); self.executor.spawn_blocking(move || { + let _enter = span.entered(); prewarm_task.run(transactions, to_prewarm_task); }); } @@ -377,7 +381,7 @@ where /// /// If the given hash is different then what is recently cached, then this will create a new /// instance. - #[instrument(target = "engine::caching", skip(self))] + #[instrument(level = "debug", target = "engine::caching", skip(self))] fn cache_for(&self, parent_hash: B256) -> SavedCache { if let Some(cache) = self.execution_cache.get_cache_for(parent_hash) { debug!("reusing execution cache"); @@ -390,6 +394,7 @@ where } /// Spawns the [`SparseTrieTask`] for this payload processor. + #[instrument(level = "debug", target = "engine::tree::payload_processor", skip_all)] fn spawn_sparse_trie_task( &self, sparse_trie_rx: mpsc::Receiver, @@ -428,13 +433,18 @@ where sparse_state_trie, ); + let span = tracing::Span::current(); self.executor.spawn_blocking(move || { + let _enter = span.entered(); + let (result, trie) = task.run(); // Send state root computation result let _ = state_root_tx.send(result); - // Clear the SparseStateTrie and replace it back into the mutex _after_ sending results - // to the next step, so that time spent clearing doesn't block the step after this one. + // Clear the SparseStateTrie and replace it back into the mutex _after_ sending + // results to the next step, so that time spent clearing doesn't block the step after + // this one. + let _enter = debug_span!(target: "engine::tree::payload_processor", "clear").entered(); cleared_sparse_trie.lock().replace(ClearedSparseStateTrie::from_state_trie(trie)); }); } @@ -459,6 +469,7 @@ impl PayloadHandle { /// # Panics /// /// If payload processing was started without background tasks. + #[instrument(level = "debug", target = "engine::tree::payload_processor", skip_all)] pub fn state_root(&mut self) -> Result { self.state_root .take() diff --git a/crates/engine/tree/src/tree/payload_processor/multiproof.rs b/crates/engine/tree/src/tree/payload_processor/multiproof.rs index 4a71bf620f7..163714483fd 100644 --- a/crates/engine/tree/src/tree/payload_processor/multiproof.rs +++ b/crates/engine/tree/src/tree/payload_processor/multiproof.rs @@ -32,7 +32,7 @@ use std::{ }, time::{Duration, Instant}, }; -use tracing::{debug, error, trace}; +use tracing::{debug, error, instrument, trace}; /// A trie update that can be applied to sparse trie alongside the proofs for touched parts of the /// state. @@ -553,7 +553,7 @@ impl MultiproofManager { let proof_result: Result = (|| { let receiver = account_proof_worker_handle - .queue_account_multiproof(input) + .dispatch_account_multiproof(input) .map_err(|e| ParallelStateRootError::Other(e.to_string()))?; receiver @@ -718,6 +718,7 @@ impl MultiProofTask { /// Handles request for proof prefetch. /// /// Returns a number of proofs that were spawned. + #[instrument(level = "debug", target = "engine::tree::payload_processor::multiproof", skip_all, fields(accounts = targets.len()))] fn on_prefetch_proof(&mut self, targets: MultiProofTargets) -> u64 { let proof_targets = self.get_prefetch_proof_targets(targets); self.fetched_proof_targets.extend_ref(&proof_targets); @@ -844,6 +845,7 @@ impl MultiProofTask { /// Handles state updates. /// /// Returns a number of proofs that were spawned. + #[instrument(level = "debug", target = "engine::tree::payload_processor::multiproof", skip(self, update), fields(accounts = update.len()))] fn on_state_update(&mut self, source: StateChangeSource, update: EvmState) -> u64 { let hashed_state_update = evm_state_to_hashed_post_state(update); @@ -973,6 +975,7 @@ impl MultiProofTask { /// currently being calculated, or if there are any pending proofs in the proof sequencer /// left to be revealed by checking the pending tasks. /// 6. This task exits after all pending proofs are processed. + #[instrument(level = "debug", target = "engine::tree::payload_processor::multiproof", skip_all)] pub(crate) fn run(mut self) { // TODO convert those into fields let mut prefetch_proofs_requested = 0; @@ -1228,8 +1231,7 @@ mod tests { ); let consistent_view = ConsistentDbView::new(factory, None); let proof_handle = - ProofWorkerHandle::new(executor.handle().clone(), consistent_view, task_ctx, 1, 1) - .expect("Failed to spawn proof workers"); + ProofWorkerHandle::new(executor.handle().clone(), consistent_view, task_ctx, 1, 1); let channel = channel(); MultiProofTask::new(config, executor, proof_handle, channel.0, 1, None) diff --git a/crates/engine/tree/src/tree/payload_processor/prewarm.rs b/crates/engine/tree/src/tree/payload_processor/prewarm.rs index 44293614d3d..de8a88a167b 100644 --- a/crates/engine/tree/src/tree/payload_processor/prewarm.rs +++ b/crates/engine/tree/src/tree/payload_processor/prewarm.rs @@ -39,7 +39,7 @@ use std::{ }, time::Instant, }; -use tracing::{debug, trace, warn}; +use tracing::{debug, debug_span, instrument, trace, warn}; /// A wrapper for transactions that includes their index in the block. #[derive(Clone)] @@ -139,8 +139,11 @@ where let ctx = self.ctx.clone(); let max_concurrency = self.max_concurrency; let transaction_count_hint = self.transaction_count_hint; + let span = tracing::Span::current(); self.executor.spawn_blocking(move || { + let _enter = debug_span!(target: "engine::tree::payload_processor::prewarm", parent: span, "spawn_all").entered(); + let (done_tx, done_rx) = mpsc::channel(); let mut executing = 0usize; @@ -157,8 +160,8 @@ where }; // Only spawn initial workers as needed - for _ in 0..workers_needed { - handles.push(ctx.spawn_worker(&executor, actions_tx.clone(), done_tx.clone())); + for i in 0..workers_needed { + handles.push(ctx.spawn_worker(i, &executor, actions_tx.clone(), done_tx.clone())); } let mut tx_index = 0usize; @@ -248,6 +251,7 @@ where /// the new, warmed cache to be inserted. /// /// This method is called from `run()` only after all execution tasks are complete. + #[instrument(level = "debug", target = "engine::tree::payload_processor::prewarm", skip_all)] fn save_cache(self, state: BundleState) { let start = Instant::now(); @@ -284,6 +288,12 @@ where /// /// This will execute the transactions until all transactions have been processed or the task /// was cancelled. + #[instrument( + level = "debug", + target = "engine::tree::payload_processor::prewarm", + name = "prewarm", + skip_all + )] pub(super) fn run( self, pending: mpsc::Receiver + Clone + Send + 'static>, @@ -364,6 +374,7 @@ where { /// Splits this context into an evm, an evm config, metrics, and the atomic bool for terminating /// execution. + #[instrument(level = "debug", target = "engine::tree::payload_processor::prewarm", skip_all)] fn evm_for_ctx(self) -> Option<(EvmFor, PrewarmMetrics, Arc)> { let Self { env, @@ -380,7 +391,7 @@ where Ok(provider) => provider, Err(err) => { trace!( - target: "engine::tree", + target: "engine::tree::payload_processor::prewarm", %err, "Failed to build state provider in prewarm thread" ); @@ -429,6 +440,7 @@ where /// /// Note: There are no ordering guarantees; this does not reflect the state produced by /// sequential execution. + #[instrument(level = "debug", target = "engine::tree::payload_processor::prewarm", skip_all)] fn transact_batch( self, txs: mpsc::Receiver>, @@ -439,7 +451,15 @@ where { let Some((mut evm, metrics, terminate_execution)) = self.evm_for_ctx() else { return }; - while let Ok(IndexedTransaction { index, tx }) = txs.recv() { + while let Ok(IndexedTransaction { index, tx }) = { + let _enter = debug_span!(target: "engine::tree::payload_processor::prewarm", "recv tx") + .entered(); + txs.recv() + } { + let _enter = + debug_span!(target: "engine::tree::payload_processor::prewarm", "prewarm tx", index, tx_hash=%tx.tx().tx_hash()) + .entered(); + // If the task was cancelled, stop execution, send an empty result to notify the task, // and exit. if terminate_execution.load(Ordering::Relaxed) { @@ -467,12 +487,18 @@ where }; metrics.execution_duration.record(start.elapsed()); + drop(_enter); + // Only send outcome for transactions after the first txn // as the main execution will be just as fast if index > 0 { + let _enter = + debug_span!(target: "engine::tree::payload_processor::prewarm", "prewarm outcome", index, tx_hash=%tx.tx().tx_hash()) + .entered(); let (targets, storage_targets) = multiproof_targets_from_state(res.state); metrics.prefetch_storage_targets.record(storage_targets as f64); let _ = sender.send(PrewarmTaskEvent::Outcome { proof_targets: Some(targets) }); + drop(_enter); } metrics.total_runtime.record(start.elapsed()); @@ -485,6 +511,7 @@ where /// Spawns a worker task for transaction execution and returns its sender channel. fn spawn_worker( &self, + idx: usize, executor: &WorkloadExecutor, actions_tx: Sender, done_tx: Sender<()>, @@ -494,8 +521,11 @@ where { let (tx, rx) = mpsc::channel(); let ctx = self.clone(); + let span = + debug_span!(target: "engine::tree::payload_processor::prewarm", "prewarm worker", idx); executor.spawn_blocking(move || { + let _enter = span.entered(); ctx.transact_batch(rx, actions_tx, done_tx); }); diff --git a/crates/engine/tree/src/tree/payload_processor/sparse_trie.rs b/crates/engine/tree/src/tree/payload_processor/sparse_trie.rs index c16f7b6e4f4..6302abde5fb 100644 --- a/crates/engine/tree/src/tree/payload_processor/sparse_trie.rs +++ b/crates/engine/tree/src/tree/payload_processor/sparse_trie.rs @@ -15,7 +15,7 @@ use std::{ sync::mpsc, time::{Duration, Instant}, }; -use tracing::{debug, trace, trace_span}; +use tracing::{debug, debug_span, instrument, trace}; /// A task responsible for populating the sparse trie. pub(super) struct SparseTrieTask @@ -61,6 +61,11 @@ where /// /// - State root computation outcome. /// - `SparseStateTrie` that needs to be cleared and reused to avoid reallocations. + #[instrument( + level = "debug", + target = "engine::tree::payload_processor::sparse_trie", + skip_all + )] pub(super) fn run( mut self, ) -> (Result, SparseStateTrie) { @@ -80,10 +85,14 @@ where while let Ok(mut update) = self.updates.recv() { num_iterations += 1; let mut num_updates = 1; + let _enter = + debug_span!(target: "engine::tree::payload_processor::sparse_trie", "drain updates") + .entered(); while let Ok(next) = self.updates.try_recv() { update.extend(next); num_updates += 1; } + drop(_enter); debug!( target: "engine::root", @@ -130,6 +139,7 @@ pub struct StateRootComputeOutcome { } /// Updates the sparse trie with the given proofs and state, and returns the elapsed time. +#[instrument(level = "debug", target = "engine::tree::payload_processor::sparse_trie", skip_all)] pub(crate) fn update_sparse_trie( trie: &mut SparseStateTrie, SparseTrieUpdate { mut state, multiproof }: SparseTrieUpdate, @@ -155,6 +165,7 @@ where ); // Update storage slots with new values and calculate storage roots. + let span = tracing::Span::current(); let (tx, rx) = mpsc::channel(); state .storages @@ -162,14 +173,16 @@ where .map(|(address, storage)| (address, storage, trie.take_storage_trie(&address))) .par_bridge() .map(|(address, storage, storage_trie)| { - let span = trace_span!(target: "engine::root::sparse", "Storage trie", ?address); - let _enter = span.enter(); - trace!(target: "engine::root::sparse", "Updating storage"); + let _enter = + debug_span!(target: "engine::tree::payload_processor::sparse_trie", parent: span.clone(), "storage trie", ?address) + .entered(); + + trace!(target: "engine::tree::payload_processor::sparse_trie", "Updating storage"); let storage_provider = blinded_provider_factory.storage_node_provider(address); let mut storage_trie = storage_trie.ok_or(SparseTrieErrorKind::Blind)?; if storage.wiped { - trace!(target: "engine::root::sparse", "Wiping storage"); + trace!(target: "engine::tree::payload_processor::sparse_trie", "Wiping storage"); storage_trie.wipe()?; } @@ -187,7 +200,7 @@ where continue; } - trace!(target: "engine::root::sparse", ?slot_nibbles, "Updating storage slot"); + trace!(target: "engine::tree::payload_processor::sparse_trie", ?slot_nibbles, "Updating storage slot"); storage_trie.update_leaf( slot_nibbles, alloy_rlp::encode_fixed_size(&value).to_vec(), @@ -219,6 +232,9 @@ where let mut removed_accounts = Vec::new(); // Update account storage roots + let _enter = + tracing::debug_span!(target: "engine::tree::payload_processor::sparse_trie", "account trie") + .entered(); for result in rx { let (address, storage_trie) = result?; trie.insert_storage_trie(address, storage_trie); diff --git a/crates/engine/tree/src/tree/payload_validator.rs b/crates/engine/tree/src/tree/payload_validator.rs index 17dc511a445..253c6c0e183 100644 --- a/crates/engine/tree/src/tree/payload_validator.rs +++ b/crates/engine/tree/src/tree/payload_validator.rs @@ -16,9 +16,7 @@ use alloy_consensus::transaction::Either; use alloy_eips::{eip1898::BlockWithParent, NumHash}; use alloy_evm::Evm; use alloy_primitives::B256; -use reth_chain_state::{ - CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates, -}; +use reth_chain_state::{CanonicalInMemoryState, ExecutedBlock}; use reth_consensus::{ConsensusError, FullConsensus}; use reth_engine_primitives::{ ConfigureEngineEvm, ExecutableTxIterator, ExecutionPayload, InvalidBlockHook, PayloadValidator, @@ -35,16 +33,19 @@ use reth_primitives_traits::{ AlloyBlockHeader, BlockTy, GotExpected, NodePrimitives, RecoveredBlock, SealedHeader, }; use reth_provider::{ - BlockExecutionOutput, BlockHashReader, BlockNumReader, BlockReader, DBProvider, - DatabaseProviderFactory, ExecutionOutcome, HashedPostStateProvider, HeaderProvider, - ProviderError, StateProvider, StateProviderFactory, StateReader, StateRootProvider, + BlockExecutionOutput, BlockNumReader, BlockReader, DBProvider, DatabaseProviderFactory, + ExecutionOutcome, HashedPostStateProvider, ProviderError, StateProvider, StateProviderFactory, + StateReader, StateRootProvider, TrieReader, }; use reth_revm::db::State; -use reth_trie::{updates::TrieUpdates, HashedPostState, KeccakKeyHasher, TrieInput}; +use reth_trie::{ + updates::{TrieUpdates, TrieUpdatesSorted}, + HashedPostState, KeccakKeyHasher, TrieInput, +}; use reth_trie_db::DatabaseHashedPostState; use reth_trie_parallel::root::{ParallelStateRoot, ParallelStateRootError}; use std::{collections::HashMap, sync::Arc, time::Instant}; -use tracing::{debug, debug_span, error, info, trace, warn}; +use tracing::{debug, debug_span, error, info, instrument, trace, warn}; /// Context providing access to tree state during validation. /// @@ -166,7 +167,7 @@ where impl BasicEngineValidator where N: NodePrimitives, - P: DatabaseProviderFactory + P: DatabaseProviderFactory + BlockReader

+ StateProviderFactory + StateReader @@ -282,12 +283,12 @@ where input: BlockOrPayload, execution_err: InsertBlockErrorKind, parent_block: &SealedHeader, - ) -> Result, InsertPayloadError> + ) -> Result, InsertPayloadError> where V: PayloadValidator, { debug!( - target: "engine::tree", + target: "engine::tree::payload_validator", ?execution_err, block = ?input.num_hash(), "Block execution failed, checking for header validation errors" @@ -322,6 +323,15 @@ where /// - Block execution /// - State root computation /// - Fork detection + #[instrument( + level = "debug", + target = "engine::tree::payload_validator", + skip_all, + fields( + parent = ?input.parent_hash(), + block_num_hash = ?input.num_hash() + ) + )] pub fn validate_block_with_state>>( &mut self, input: BlockOrPayload, @@ -364,7 +374,9 @@ where let parent_hash = input.parent_hash(); let block_num_hash = input.num_hash(); - trace!(target: "engine::tree", block=?block_num_hash, parent=?parent_hash, "Fetching block state provider"); + trace!(target: "engine::tree::payload_validator", "Fetching block state provider"); + let _enter = + debug_span!(target: "engine::tree::payload_validator", "state provider").entered(); let Some(provider_builder) = ensure_ok!(self.state_provider_builder(parent_hash, ctx.state())) else { @@ -375,8 +387,8 @@ where ) .into()) }; - let state_provider = ensure_ok!(provider_builder.build()); + drop(_enter); // fetch parent block let Some(parent_block) = ensure_ok!(self.sealed_header_by_hash(parent_hash, ctx.state())) @@ -388,22 +400,20 @@ where .into()) }; - let evm_env = self.evm_env_for(&input).map_err(NewPayloadError::other)?; + let evm_env = debug_span!(target: "engine::tree::payload_validator", "evm env") + .in_scope(|| self.evm_env_for(&input)) + .map_err(NewPayloadError::other)?; let env = ExecutionEnv { evm_env, hash: input.hash(), parent_hash: input.parent_hash() }; // Plan the strategy used for state root computation. let state_root_plan = self.plan_state_root_computation(&input, &ctx); let persisting_kind = state_root_plan.persisting_kind; - let has_ancestors_with_missing_trie_updates = - state_root_plan.has_ancestors_with_missing_trie_updates; let strategy = state_root_plan.strategy; debug!( - target: "engine::tree", - block=?block_num_hash, + target: "engine::tree::payload_validator", ?strategy, - ?has_ancestors_with_missing_trie_updates, "Deciding which state root algorithm to run" ); @@ -418,7 +428,6 @@ where persisting_kind, parent_hash, ctx.state(), - block_num_hash, strategy, )); @@ -453,7 +462,7 @@ where block ); - debug!(target: "engine::tree", block=?block_num_hash, "Calculating block state root"); + debug!(target: "engine::tree::payload_validator", "Calculating block state root"); let root_time = Instant::now(); @@ -461,17 +470,17 @@ where match strategy { StateRootStrategy::StateRootTask => { - debug!(target: "engine::tree", block=?block_num_hash, "Using sparse trie state root algorithm"); + debug!(target: "engine::tree::payload_validator", "Using sparse trie state root algorithm"); match handle.state_root() { Ok(StateRootComputeOutcome { state_root, trie_updates }) => { let elapsed = root_time.elapsed(); - info!(target: "engine::tree", ?state_root, ?elapsed, "State root task finished"); + info!(target: "engine::tree::payload_validator", ?state_root, ?elapsed, "State root task finished"); // we double check the state root here for good measure if state_root == block.header().state_root() { maybe_state_root = Some((state_root, trie_updates, elapsed)) } else { warn!( - target: "engine::tree", + target: "engine::tree::payload_validator", ?state_root, block_state_root = ?block.header().state_root(), "State root task returned incorrect state root" @@ -479,12 +488,12 @@ where } } Err(error) => { - debug!(target: "engine::tree", %error, "State root task failed"); + debug!(target: "engine::tree::payload_validator", %error, "State root task failed"); } } } StateRootStrategy::Parallel => { - debug!(target: "engine::tree", block=?block_num_hash, "Using parallel state root algorithm"); + debug!(target: "engine::tree::payload_validator", "Using parallel state root algorithm"); match self.compute_state_root_parallel( persisting_kind, block.parent_hash(), @@ -494,8 +503,7 @@ where Ok(result) => { let elapsed = root_time.elapsed(); info!( - target: "engine::tree", - block = ?block_num_hash, + target: "engine::tree::payload_validator", regular_state_root = ?result.0, ?elapsed, "Regular root task finished" @@ -503,7 +511,7 @@ where maybe_state_root = Some((result.0, result.1, elapsed)); } Err(error) => { - debug!(target: "engine::tree", %error, "Parallel state root computation failed"); + debug!(target: "engine::tree::payload_validator", %error, "Parallel state root computation failed"); } } } @@ -520,9 +528,9 @@ where } else { // fallback is to compute the state root regularly in sync if self.config.state_root_fallback() { - debug!(target: "engine::tree", block=?block_num_hash, "Using state root fallback for testing"); + debug!(target: "engine::tree::payload_validator", "Using state root fallback for testing"); } else { - warn!(target: "engine::tree", block=?block_num_hash, ?persisting_kind, "Failed to compute state root in parallel"); + warn!(target: "engine::tree::payload_validator", ?persisting_kind, "Failed to compute state root in parallel"); self.metrics.block_validation.state_root_parallel_fallback_total.increment(1); } @@ -534,7 +542,7 @@ where }; self.metrics.block_validation.record_state_root(&trie_output, root_elapsed.as_secs_f64()); - debug!(target: "engine::tree", ?root_elapsed, block=?block_num_hash, "Calculated state root"); + debug!(target: "engine::tree::payload_validator", ?root_elapsed, "Calculated state root"); // ensure state root matches if state_root != block.header().state_root() { @@ -560,38 +568,11 @@ where // terminate prewarming task with good state output handle.terminate_caching(Some(&output.state)); - // If the block doesn't connect to the database tip, we don't save its trie updates, because - // they may be incorrect as they were calculated on top of the forked block. - // - // We also only save trie updates if all ancestors have trie updates, because otherwise the - // trie updates may be incorrect. - // - // Instead, they will be recomputed on persistence. - let connects_to_last_persisted = - ensure_ok_post_block!(self.block_connects_to_last_persisted(ctx, &block), block); - let should_discard_trie_updates = - !connects_to_last_persisted || has_ancestors_with_missing_trie_updates; - debug!( - target: "engine::tree", - block = ?block_num_hash, - connects_to_last_persisted, - has_ancestors_with_missing_trie_updates, - should_discard_trie_updates, - "Checking if should discard trie updates" - ); - let trie_updates = if should_discard_trie_updates { - ExecutedTrieUpdates::Missing - } else { - ExecutedTrieUpdates::Present(Arc::new(trie_output)) - }; - - Ok(ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { - recovered_block: Arc::new(block), - execution_output: Arc::new(ExecutionOutcome::from((output, block_num_hash.number))), - hashed_state: Arc::new(hashed_state), - }, - trie: trie_updates, + Ok(ExecutedBlock { + recovered_block: Arc::new(block), + execution_output: Arc::new(ExecutionOutcome::from((output, block_num_hash.number))), + hashed_state: Arc::new(hashed_state), + trie_updates: Arc::new(trie_output), }) } @@ -615,12 +596,12 @@ where /// and block body itself. fn validate_block_inner(&self, block: &RecoveredBlock) -> Result<(), ConsensusError> { if let Err(e) = self.consensus.validate_header(block.sealed_header()) { - error!(target: "engine::tree", ?block, "Failed to validate header {}: {e}", block.hash()); + error!(target: "engine::tree::payload_validator", ?block, "Failed to validate header {}: {e}", block.hash()); return Err(e) } if let Err(e) = self.consensus.validate_block_pre_execution(block.sealed_block()) { - error!(target: "engine::tree", ?block, "Failed to validate block {}: {e}", block.hash()); + error!(target: "engine::tree::payload_validator", ?block, "Failed to validate block {}: {e}", block.hash()); return Err(e) } @@ -628,6 +609,7 @@ where } /// Executes a block with the given state provider + #[instrument(level = "debug", target = "engine::tree::payload_validator", skip_all)] fn execute_block( &mut self, state_provider: S, @@ -642,11 +624,7 @@ where T: PayloadTypes>, Evm: ConfigureEngineEvm, { - let num_hash = NumHash::new(env.evm_env.block_env.number.to(), env.hash); - - let span = debug_span!(target: "engine::tree", "execute_block", num = ?num_hash.number, hash = ?num_hash.hash); - let _enter = span.enter(); - debug!(target: "engine::tree", "Executing block"); + debug!(target: "engine::tree::payload_validator", "Executing block"); let mut db = State::builder() .with_database(StateProviderDatabase::new(&state_provider)) @@ -685,7 +663,7 @@ where )?; let execution_finish = Instant::now(); let execution_time = execution_finish.duration_since(execution_start); - debug!(target: "engine::tree", elapsed = ?execution_time, number=?num_hash.number, "Executed block"); + debug!(target: "engine::tree::payload_validator", elapsed = ?execution_time, "Executed block"); Ok(output) } @@ -697,6 +675,7 @@ where /// Returns `Err(_)` if error was encountered during computation. /// `Err(ProviderError::ConsistentView(_))` can be safely ignored and fallback computation /// should be used instead. + #[instrument(level = "debug", target = "engine::tree::payload_validator", skip_all)] fn compute_state_root_parallel( &self, persisting_kind: PersistingKind, @@ -719,51 +698,6 @@ where ParallelStateRoot::new(consistent_view, input).incremental_root_with_updates() } - /// Checks if the given block connects to the last persisted block, i.e. if the last persisted - /// block is the ancestor of the given block. - /// - /// This checks the database for the actual last persisted block, not [`PersistenceState`]. - fn block_connects_to_last_persisted( - &self, - ctx: TreeCtx<'_, N>, - block: &RecoveredBlock, - ) -> ProviderResult { - let provider = self.provider.database_provider_ro()?; - let last_persisted_block = provider.best_block_number()?; - let last_persisted_hash = provider - .block_hash(last_persisted_block)? - .ok_or(ProviderError::HeaderNotFound(last_persisted_block.into()))?; - let last_persisted = NumHash::new(last_persisted_block, last_persisted_hash); - - let parent_num_hash = |hash: B256| -> ProviderResult { - let parent_num_hash = - if let Some(header) = ctx.state().tree_state.sealed_header_by_hash(&hash) { - Some(header.parent_num_hash()) - } else { - provider.sealed_header_by_hash(hash)?.map(|header| header.parent_num_hash()) - }; - - parent_num_hash.ok_or(ProviderError::BlockHashNotFound(hash)) - }; - - let mut parent_block = block.parent_num_hash(); - while parent_block.number > last_persisted.number { - parent_block = parent_num_hash(parent_block.hash)?; - } - - let connects = parent_block == last_persisted; - - debug!( - target: "engine::tree", - num_hash = ?block.num_hash(), - ?last_persisted, - ?parent_block, - "Checking if block connects to last persisted block" - ); - - Ok(connects) - } - /// Validates the block after execution. /// /// This performs: @@ -782,7 +716,7 @@ where { let start = Instant::now(); - trace!(target: "engine::tree", block=?block.num_hash(), "Validating block consensus"); + trace!(target: "engine::tree::payload_validator", block=?block.num_hash(), "Validating block consensus"); // validate block consensus rules if let Err(e) = self.validate_block_inner(block) { return Err(e.into()) @@ -792,7 +726,7 @@ where if let Err(e) = self.consensus.validate_header_against_parent(block.sealed_header(), parent_block) { - warn!(target: "engine::tree", ?block, "Failed to validate header {} against parent: {e}", block.hash()); + warn!(target: "engine::tree::payload_validator", ?block, "Failed to validate header {} against parent: {e}", block.hash()); return Err(e.into()) } @@ -832,6 +766,12 @@ where /// The method handles strategy fallbacks if the preferred approach fails, ensuring /// block execution always completes with a valid state root. #[allow(clippy::too_many_arguments)] + #[instrument( + level = "debug", + target = "engine::tree::payload_validator", + skip_all, + fields(strategy) + )] fn spawn_payload_processor>( &mut self, env: ExecutionEnv, @@ -840,7 +780,6 @@ where persisting_kind: PersistingKind, parent_hash: B256, state: &EngineApiTreeState, - block_num_hash: NumHash, strategy: StateRootStrategy, ) -> Result< ( @@ -894,8 +833,7 @@ where Err((error, txs, env, provider_builder)) => { // Failed to spawn proof workers, fallback to parallel state root error!( - target: "engine::tree", - block=?block_num_hash, + target: "engine::tree::payload_validator", ?error, "Failed to spawn proof workers, falling back to parallel state root" ); @@ -913,8 +851,7 @@ where // prewarming for transaction execution } else { debug!( - target: "engine::tree", - block=?block_num_hash, + target: "engine::tree::payload_validator", "Disabling state root task due to non-empty prefix sets" ); ( @@ -947,27 +884,6 @@ where } } - /// Check if the given block has any ancestors with missing trie updates. - fn has_ancestors_with_missing_trie_updates( - &self, - target_header: BlockWithParent, - state: &EngineApiTreeState, - ) -> bool { - // Walk back through the chain starting from the parent of the target block - let mut current_hash = target_header.parent; - while let Some(block) = state.tree_state.blocks_by_hash.get(¤t_hash) { - // Check if this block is missing trie updates - if block.trie.is_missing() { - return true; - } - - // Move to the parent block - current_hash = block.recovered_block().parent_hash(); - } - - false - } - /// Creates a `StateProviderBuilder` for the given parent hash. /// /// This method checks if the parent is in the tree state (in-memory) or persisted to disk, @@ -978,7 +894,7 @@ where state: &EngineApiTreeState, ) -> ProviderResult>> { if let Some((historical, blocks)) = state.tree_state.blocks_by_hash(hash) { - debug!(target: "engine::tree", %hash, %historical, "found canonical state for block in memory, creating provider builder"); + debug!(target: "engine::tree::payload_validator", %hash, %historical, "found canonical state for block in memory, creating provider builder"); // the block leads back to the canonical chain return Ok(Some(StateProviderBuilder::new( self.provider.clone(), @@ -989,17 +905,18 @@ where // Check if the block is persisted if let Some(header) = self.provider.header(hash)? { - debug!(target: "engine::tree", %hash, number = %header.number(), "found canonical state for block in database, creating provider builder"); + debug!(target: "engine::tree::payload_validator", %hash, number = %header.number(), "found canonical state for block in database, creating provider builder"); // For persisted blocks, we create a builder that will fetch state directly from the // database return Ok(Some(StateProviderBuilder::new(self.provider.clone(), hash, None))) } - debug!(target: "engine::tree", %hash, "no canonical state found for block"); + debug!(target: "engine::tree::payload_validator", %hash, "no canonical state found for block"); Ok(None) } /// Determines the state root computation strategy based on persistence state and configuration. + #[instrument(level = "debug", target = "engine::tree::payload_validator", skip_all)] fn plan_state_root_computation>>( &self, input: &BlockOrPayload, @@ -1018,20 +935,12 @@ where let can_run_parallel = persisting_kind.can_run_parallel_state_root() && !self.config.state_root_fallback(); - // Check for ancestors with missing trie updates - let has_ancestors_with_missing_trie_updates = - self.has_ancestors_with_missing_trie_updates(input.block_with_parent(), ctx.state()); - // Decide on the strategy. // Use state root task only if: // 1. No persistence is in progress // 2. Config allows it - // 3. No ancestors with missing trie updates. If any exist, it will mean that every state - // root task proof calculation will include a lot of unrelated paths in the prefix sets. - // It's cheaper to run a parallel state root that does one walk over trie tables while - // accounting for the prefix sets. let strategy = if can_run_parallel { - if self.config.use_state_root_task() && !has_ancestors_with_missing_trie_updates { + if self.config.use_state_root_task() { StateRootStrategy::StateRootTask } else { StateRootStrategy::Parallel @@ -1041,14 +950,13 @@ where }; debug!( - target: "engine::tree", + target: "engine::tree::payload_validator", block=?input.num_hash(), ?strategy, - has_ancestors_with_missing_trie_updates, "Planned state root computation strategy" ); - StateRootPlan { strategy, has_ancestors_with_missing_trie_updates, persisting_kind } + StateRootPlan { strategy, persisting_kind } } /// Called when an invalid block is encountered during validation. @@ -1082,7 +990,13 @@ where /// block. /// 3. Once in-memory blocks are collected and optionally filtered, we compute the /// [`HashedPostState`] from them. - fn compute_trie_input( + #[instrument( + level = "debug", + target = "engine::tree::payload_validator", + skip_all, + fields(persisting_kind, parent_hash) + )] + fn compute_trie_input( &self, persisting_kind: PersistingKind, provider: TP, @@ -1102,6 +1016,9 @@ where // If the current block is a descendant of the currently persisting blocks, then we need to // filter in-memory blocks, so that none of them are already persisted in the database. + let _enter = + debug_span!(target: "engine::tree::payload_validator", "filter in-memory blocks", len = blocks.len()) + .entered(); if persisting_kind.is_descendant() { // Iterate over the blocks from oldest to newest. while let Some(block) = blocks.last() { @@ -1126,11 +1043,13 @@ where parent_hash.into() }; } + drop(_enter); - if blocks.is_empty() { - debug!(target: "engine::tree", %parent_hash, "Parent found on disk"); + let blocks_empty = blocks.is_empty(); + if blocks_empty { + debug!(target: "engine::tree::payload_validator", "Parent found on disk"); } else { - debug!(target: "engine::tree", %parent_hash, %historical, blocks = blocks.len(), "Parent found in memory"); + debug!(target: "engine::tree::payload_validator", %historical, blocks = blocks.len(), "Parent found in memory"); } // Convert the historical block to the block number. @@ -1138,29 +1057,35 @@ where .convert_hash_or_number(historical)? .ok_or_else(|| ProviderError::BlockHashNotFound(historical.as_hash().unwrap()))?; + let _enter = + debug_span!(target: "engine::tree::payload_validator", "revert state", blocks_empty) + .entered(); // Retrieve revert state for historical block. - let revert_state = if block_number == best_block_number { + let (revert_state, revert_trie) = if block_number == best_block_number { // We do not check against the `last_block_number` here because - // `HashedPostState::from_reverts` only uses the database tables, and not static files. - debug!(target: "engine::tree", block_number, best_block_number, "Empty revert state"); - HashedPostState::default() + // `HashedPostState::from_reverts` / `trie_reverts` only use the database tables, and + // not static files. + debug!(target: "engine::tree::payload_validator", block_number, best_block_number, "Empty revert state"); + (HashedPostState::default(), TrieUpdatesSorted::default()) } else { let revert_state = HashedPostState::from_reverts::( provider.tx_ref(), block_number + 1.., ) .map_err(ProviderError::from)?; + let revert_trie = provider.trie_reverts(block_number + 1)?; debug!( - target: "engine::tree", + target: "engine::tree::payload_validator", block_number, best_block_number, accounts = revert_state.accounts.len(), storages = revert_state.storages.len(), "Non-empty revert state" ); - revert_state + (revert_state, revert_trie) }; - input.append(revert_state); + + input.append_cached(revert_trie.into(), revert_state); // Extend with contents of parent in-memory blocks. input.extend_with_blocks( @@ -1172,8 +1097,7 @@ where } /// Output of block or payload validation. -pub type ValidationOutcome>> = - Result, E>; +pub type ValidationOutcome>> = Result, E>; /// Strategy describing how to compute the state root. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -1190,8 +1114,6 @@ enum StateRootStrategy { struct StateRootPlan { /// Strategy that should be attempted for computing the state root. strategy: StateRootStrategy, - /// Whether ancestors have missing trie updates. - has_ancestors_with_missing_trie_updates: bool, /// The persisting kind for this block. persisting_kind: PersistingKind, } @@ -1249,7 +1171,7 @@ pub trait EngineValidator< impl EngineValidator for BasicEngineValidator where - P: DatabaseProviderFactory + P: DatabaseProviderFactory + BlockReader
+ StateProviderFactory + StateReader diff --git a/crates/engine/tree/src/tree/state.rs b/crates/engine/tree/src/tree/state.rs index cab7d35fb22..f38faf6524c 100644 --- a/crates/engine/tree/src/tree/state.rs +++ b/crates/engine/tree/src/tree/state.rs @@ -1,29 +1,19 @@ //! Functionality related to tree state. use crate::engine::EngineApiKind; -use alloy_eips::{eip1898::BlockWithParent, merge::EPOCH_SLOTS, BlockNumHash}; +use alloy_eips::{eip1898::BlockWithParent, BlockNumHash}; use alloy_primitives::{ map::{HashMap, HashSet}, BlockNumber, B256, }; -use reth_chain_state::{EthPrimitives, ExecutedBlockWithTrieUpdates}; +use reth_chain_state::{EthPrimitives, ExecutedBlock}; use reth_primitives_traits::{AlloyBlockHeader, NodePrimitives, SealedHeader}; -use reth_trie::updates::TrieUpdates; use std::{ collections::{btree_map, hash_map, BTreeMap, VecDeque}, ops::Bound, - sync::Arc, }; use tracing::debug; -/// Default number of blocks to retain persisted trie updates -const DEFAULT_PERSISTED_TRIE_UPDATES_RETENTION: u64 = EPOCH_SLOTS * 2; - -/// Number of blocks to retain persisted trie updates for OP Stack chains -/// OP Stack chains only need `EPOCH_SLOTS` as reorgs are relevant only when -/// op-node reorgs to the same chain twice -const OPSTACK_PERSISTED_TRIE_UPDATES_RETENTION: u64 = EPOCH_SLOTS; - /// Keeps track of the state of the tree. /// /// ## Invariants @@ -35,19 +25,15 @@ pub struct TreeState { /// __All__ unique executed blocks by block hash that are connected to the canonical chain. /// /// This includes blocks of all forks. - pub(crate) blocks_by_hash: HashMap>, + pub(crate) blocks_by_hash: HashMap>, /// Executed blocks grouped by their respective block number. /// /// This maps unique block number to all known blocks for that height. /// /// Note: there can be multiple blocks at the same height due to forks. - pub(crate) blocks_by_number: BTreeMap>>, + pub(crate) blocks_by_number: BTreeMap>>, /// Map of any parent block hash to its children. pub(crate) parent_to_child: HashMap>, - /// Map of hash to trie updates for canonical blocks that are persisted but not finalized. - /// - /// Contains the block number for easy removal. - pub(crate) persisted_trie_updates: HashMap)>, /// Currently tracked canonical head of the chain. pub(crate) current_canonical_head: BlockNumHash, /// The engine API variant of this handler @@ -62,7 +48,6 @@ impl TreeState { blocks_by_number: BTreeMap::new(), current_canonical_head, parent_to_child: HashMap::default(), - persisted_trie_updates: HashMap::default(), engine_kind, } } @@ -77,11 +62,8 @@ impl TreeState { self.blocks_by_hash.len() } - /// Returns the [`ExecutedBlockWithTrieUpdates`] by hash. - pub(crate) fn executed_block_by_hash( - &self, - hash: B256, - ) -> Option<&ExecutedBlockWithTrieUpdates> { + /// Returns the [`ExecutedBlock`] by hash. + pub(crate) fn executed_block_by_hash(&self, hash: B256) -> Option<&ExecutedBlock> { self.blocks_by_hash.get(&hash) } @@ -97,10 +79,7 @@ impl TreeState { /// newest to oldest. And the parent hash of the oldest block that is missing from the buffer. /// /// Returns `None` if the block for the given hash is not found. - pub(crate) fn blocks_by_hash( - &self, - hash: B256, - ) -> Option<(B256, Vec>)> { + pub(crate) fn blocks_by_hash(&self, hash: B256) -> Option<(B256, Vec>)> { let block = self.blocks_by_hash.get(&hash).cloned()?; let mut parent_hash = block.recovered_block().parent_hash(); let mut blocks = vec![block]; @@ -113,7 +92,7 @@ impl TreeState { } /// Insert executed block into the state. - pub(crate) fn insert_executed(&mut self, executed: ExecutedBlockWithTrieUpdates) { + pub(crate) fn insert_executed(&mut self, executed: ExecutedBlock) { let hash = executed.recovered_block().hash(); let parent_hash = executed.recovered_block().parent_hash(); let block_number = executed.recovered_block().number(); @@ -138,10 +117,7 @@ impl TreeState { /// ## Returns /// /// The removed block and the block hashes of its children. - fn remove_by_hash( - &mut self, - hash: B256, - ) -> Option<(ExecutedBlockWithTrieUpdates, HashSet)> { + fn remove_by_hash(&mut self, hash: B256) -> Option<(ExecutedBlock, HashSet)> { let executed = self.blocks_by_hash.remove(&hash)?; // Remove this block from collection of children of its parent block. @@ -215,41 +191,12 @@ impl TreeState { if executed.recovered_block().number() <= upper_bound { let num_hash = executed.recovered_block().num_hash(); debug!(target: "engine::tree", ?num_hash, "Attempting to remove block walking back from the head"); - if let Some((mut removed, _)) = - self.remove_by_hash(executed.recovered_block().hash()) - { - debug!(target: "engine::tree", ?num_hash, "Removed block walking back from the head"); - // finally, move the trie updates - let Some(trie_updates) = removed.trie.take_present() else { - debug!(target: "engine::tree", ?num_hash, "No trie updates found for persisted block"); - continue; - }; - self.persisted_trie_updates.insert( - removed.recovered_block().hash(), - (removed.recovered_block().number(), trie_updates), - ); - } + self.remove_by_hash(executed.recovered_block().hash()); } } debug!(target: "engine::tree", ?upper_bound, ?last_persisted_hash, "Removed canonical blocks from the tree"); } - /// Prunes old persisted trie updates based on the current block number - /// and chain type (OP Stack or regular) - pub(crate) fn prune_persisted_trie_updates(&mut self) { - let retention_blocks = if self.engine_kind.is_opstack() { - OPSTACK_PERSISTED_TRIE_UPDATES_RETENTION - } else { - DEFAULT_PERSISTED_TRIE_UPDATES_RETENTION - }; - - let earliest_block_to_retain = - self.current_canonical_head.number.saturating_sub(retention_blocks); - - self.persisted_trie_updates - .retain(|_, (block_number, _)| *block_number > earliest_block_to_retain); - } - /// Removes all blocks that are below the finalized block, as well as removing non-canonical /// sidechains that fork from below the finalized block. pub(crate) fn prune_finalized_sidechains(&mut self, finalized_num_hash: BlockNumHash) { @@ -274,8 +221,6 @@ impl TreeState { } } - self.prune_persisted_trie_updates(); - // The only block that should remain at the `finalized` number now, is the finalized // block, if it exists. // diff --git a/crates/engine/tree/src/tree/tests.rs b/crates/engine/tree/src/tree/tests.rs index 17b5950e077..49ce5ab9cf1 100644 --- a/crates/engine/tree/src/tree/tests.rs +++ b/crates/engine/tree/src/tree/tests.rs @@ -3,6 +3,7 @@ use crate::{ persistence::PersistenceAction, tree::{ payload_validator::{BasicEngineValidator, TreeCtx, ValidationOutcome}, + persistence_state::CurrentPersistenceAction, TreeConfig, }, }; @@ -26,7 +27,7 @@ use reth_ethereum_primitives::{Block, EthPrimitives}; use reth_evm_ethereum::MockEvmConfig; use reth_primitives_traits::Block as _; use reth_provider::{test_utils::MockEthProvider, ExecutionOutcome}; -use reth_trie::HashedPostState; +use reth_trie::{updates::TrieUpdates, HashedPostState}; use std::{ collections::BTreeMap, str::FromStr, @@ -148,7 +149,7 @@ struct TestHarness { >, to_tree_tx: Sender, Block>>, from_tree_rx: UnboundedReceiver, - blocks: Vec, + blocks: Vec, action_rx: Receiver, block_builder: TestBlockBuilder, provider: MockEthProvider, @@ -228,7 +229,7 @@ impl TestHarness { } } - fn with_blocks(mut self, blocks: Vec) -> Self { + fn with_blocks(mut self, blocks: Vec) -> Self { let mut blocks_by_hash = HashMap::default(); let mut blocks_by_number = BTreeMap::new(); let mut state_by_hash = HashMap::default(); @@ -253,7 +254,6 @@ impl TestHarness { blocks_by_number, current_canonical_head: blocks.last().unwrap().recovered_block().num_hash(), parent_to_child, - persisted_trie_updates: HashMap::default(), engine_kind: EngineApiKind::Ethereum, }; @@ -405,7 +405,6 @@ impl ValidatorTestHarness { /// Configure `PersistenceState` for specific `PersistingKind` scenarios fn start_persistence_operation(&mut self, action: CurrentPersistenceAction) { - use crate::tree::persistence_state::CurrentPersistenceAction; use tokio::sync::oneshot; // Create a dummy receiver for testing - it will never receive a value @@ -828,25 +827,21 @@ fn test_tree_state_on_new_head_deep_fork() { let chain_b = test_block_builder.create_fork(&last_block, 10); for block in &chain_a { - test_harness.tree.state.tree_state.insert_executed(ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { - recovered_block: Arc::new(block.clone()), - execution_output: Arc::new(ExecutionOutcome::default()), - hashed_state: Arc::new(HashedPostState::default()), - }, - trie: ExecutedTrieUpdates::empty(), + test_harness.tree.state.tree_state.insert_executed(ExecutedBlock { + recovered_block: Arc::new(block.clone()), + execution_output: Arc::new(ExecutionOutcome::default()), + hashed_state: Arc::new(HashedPostState::default()), + trie_updates: Arc::new(TrieUpdates::default()), }); } test_harness.tree.state.tree_state.set_canonical_head(chain_a.last().unwrap().num_hash()); for block in &chain_b { - test_harness.tree.state.tree_state.insert_executed(ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { - recovered_block: Arc::new(block.clone()), - execution_output: Arc::new(ExecutionOutcome::default()), - hashed_state: Arc::new(HashedPostState::default()), - }, - trie: ExecutedTrieUpdates::empty(), + test_harness.tree.state.tree_state.insert_executed(ExecutedBlock { + recovered_block: Arc::new(block.clone()), + execution_output: Arc::new(ExecutionOutcome::default()), + hashed_state: Arc::new(HashedPostState::default()), + trie_updates: Arc::new(TrieUpdates::default()), }); } diff --git a/crates/ethereum/cli/src/app.rs b/crates/ethereum/cli/src/app.rs index 805c9144257..ab3682be6dc 100644 --- a/crates/ethereum/cli/src/app.rs +++ b/crates/ethereum/cli/src/app.rs @@ -82,10 +82,7 @@ where ) -> Result<()>, ) -> Result<()> where - N: CliNodeTypes< - Primitives: NodePrimitives, - ChainSpec: Hardforks + EthChainSpec, - >, + N: CliNodeTypes, ChainSpec: Hardforks>, C: ChainSpecParser, { let runner = match self.runner.take() { @@ -119,7 +116,7 @@ where layers.with_span_layer( "reth".to_string(), output_type.clone(), - self.cli.traces.otlp_level, + self.cli.traces.otlp_filter.clone(), )?; } diff --git a/crates/ethereum/evm/src/build.rs b/crates/ethereum/evm/src/build.rs index 5f5e014d297..85d4cae311b 100644 --- a/crates/ethereum/evm/src/build.rs +++ b/crates/ethereum/evm/src/build.rs @@ -1,7 +1,7 @@ use alloc::{sync::Arc, vec::Vec}; use alloy_consensus::{ proofs::{self, calculate_receipt_root}, - Block, BlockBody, BlockHeader, Header, Transaction, TxReceipt, EMPTY_OMMER_ROOT_HASH, + Block, BlockBody, BlockHeader, Header, TxReceipt, EMPTY_OMMER_ROOT_HASH, }; use alloy_eips::merge::BEACON_NONCE; use alloy_evm::{block::BlockExecutorFactory, eth::EthBlockExecutionCtx}; @@ -10,6 +10,7 @@ use reth_chainspec::{EthChainSpec, EthereumHardforks}; use reth_evm::execute::{BlockAssembler, BlockAssemblerInput, BlockExecutionError}; use reth_execution_types::BlockExecutionResult; use reth_primitives_traits::{logs_bloom, Receipt, SignedTransaction}; +use revm::context::Block as _; /// Block builder for Ethereum. #[derive(Debug, Clone)] @@ -47,12 +48,12 @@ where execution_ctx: ctx, parent, transactions, - output: BlockExecutionResult { receipts, requests, gas_used }, + output: BlockExecutionResult { receipts, requests, gas_used, blob_gas_used }, state_root, .. } = input; - let timestamp = evm_env.block_env.timestamp.saturating_to(); + let timestamp = evm_env.block_env.timestamp().saturating_to(); let transactions_root = proofs::calculate_transaction_root(&transactions); let receipts_root = calculate_receipt_root( @@ -73,12 +74,11 @@ where .then(|| requests.requests_hash()); let mut excess_blob_gas = None; - let mut blob_gas_used = None; + let mut block_blob_gas_used = None; // only determine cancun fields when active if self.chain_spec.is_cancun_active_at_timestamp(timestamp) { - blob_gas_used = - Some(transactions.iter().map(|tx| tx.blob_gas_used().unwrap_or_default()).sum()); + block_blob_gas_used = Some(*blob_gas_used); excess_blob_gas = if self.chain_spec.is_cancun_active_at_timestamp(parent.timestamp) { parent.maybe_next_block_excess_blob_gas( self.chain_spec.blob_params_at_timestamp(timestamp), @@ -96,23 +96,23 @@ where let header = Header { parent_hash: ctx.parent_hash, ommers_hash: EMPTY_OMMER_ROOT_HASH, - beneficiary: evm_env.block_env.beneficiary, + beneficiary: evm_env.block_env.beneficiary(), state_root, transactions_root, receipts_root, withdrawals_root, logs_bloom, timestamp, - mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(), + mix_hash: evm_env.block_env.prevrandao().unwrap_or_default(), nonce: BEACON_NONCE.into(), - base_fee_per_gas: Some(evm_env.block_env.basefee), - number: evm_env.block_env.number.saturating_to(), - gas_limit: evm_env.block_env.gas_limit, - difficulty: evm_env.block_env.difficulty, + base_fee_per_gas: Some(evm_env.block_env.basefee()), + number: evm_env.block_env.number().saturating_to(), + gas_limit: evm_env.block_env.gas_limit(), + difficulty: evm_env.block_env.difficulty(), gas_used: *gas_used, extra_data: self.extra_data.clone(), parent_beacon_block_root: ctx.parent_beacon_block_root, - blob_gas_used, + blob_gas_used: block_blob_gas_used, excess_blob_gas, requests_hash, }; diff --git a/crates/ethereum/evm/src/lib.rs b/crates/ethereum/evm/src/lib.rs index eaf91f0c7be..c0f8adc9c54 100644 --- a/crates/ethereum/evm/src/lib.rs +++ b/crates/ethereum/evm/src/lib.rs @@ -132,6 +132,7 @@ where + FromRecoveredTx + FromTxWithEncoded, Spec = SpecId, + BlockEnv = BlockEnv, Precompiles = PrecompilesMap, > + Clone + Debug @@ -154,7 +155,7 @@ where &self.block_assembler } - fn evm_env(&self, header: &Header) -> Result { + fn evm_env(&self, header: &Header) -> Result, Self::Error> { Ok(EvmEnv::for_eth_block( header, self.chain_spec(), @@ -217,6 +218,7 @@ where + FromRecoveredTx + FromTxWithEncoded, Spec = SpecId, + BlockEnv = BlockEnv, Precompiles = PrecompilesMap, > + Clone + Debug diff --git a/crates/ethereum/evm/src/test_utils.rs b/crates/ethereum/evm/src/test_utils.rs index 87875dbc848..fe791b9f5fd 100644 --- a/crates/ethereum/evm/src/test_utils.rs +++ b/crates/ethereum/evm/src/test_utils.rs @@ -125,6 +125,7 @@ impl<'a, DB: Database, I: Inspector>>> BlockExec reqs }), gas_used: 0, + blob_gas_used: 0, }; evm.db_mut().bundle_state = bundle; diff --git a/crates/ethereum/node/src/node.rs b/crates/ethereum/node/src/node.rs index 089353f6b73..74740643a41 100644 --- a/crates/ethereum/node/src/node.rs +++ b/crates/ethereum/node/src/node.rs @@ -15,7 +15,6 @@ use reth_ethereum_engine_primitives::{ use reth_ethereum_primitives::{EthPrimitives, TransactionSigned}; use reth_evm::{ eth::spec::EthExecutorSpec, ConfigureEvm, EvmFactory, EvmFactoryFor, NextBlockEnvAttributes, - SpecFor, TxEnvFor, }; use reth_network::{primitives::BasicNetworkPrimitives, NetworkHandle, PeersInfo}; use reth_node_api::{ @@ -159,10 +158,9 @@ where NetworkT: RpcTypes>>, EthRpcConverterFor: RpcConvert< Primitives = PrimitivesTy, - TxEnv = TxEnvFor, Error = EthApiError, Network = NetworkT, - Spec = SpecFor, + Evm = N::Evm, >, EthApiError: FromEvmError, { diff --git a/crates/ethereum/node/tests/e2e/blobs.rs b/crates/ethereum/node/tests/e2e/blobs.rs index 8fd9d08d2dc..1c088e33da6 100644 --- a/crates/ethereum/node/tests/e2e/blobs.rs +++ b/crates/ethereum/node/tests/e2e/blobs.rs @@ -1,15 +1,21 @@ use crate::utils::eth_payload_attributes; +use alloy_eips::Decodable2718; use alloy_genesis::Genesis; use reth_chainspec::{ChainSpecBuilder, MAINNET}; use reth_e2e_test_utils::{ node::NodeTestContext, transaction::TransactionTestContext, wallet::Wallet, }; +use reth_ethereum_engine_primitives::BlobSidecars; +use reth_ethereum_primitives::PooledTransactionVariant; use reth_node_builder::{NodeBuilder, NodeHandle}; use reth_node_core::{args::RpcServerArgs, node_config::NodeConfig}; use reth_node_ethereum::EthereumNode; use reth_tasks::TaskManager; use reth_transaction_pool::TransactionPool; -use std::sync::Arc; +use std::{ + sync::Arc, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; #[tokio::test] async fn can_handle_blobs() -> eyre::Result<()> { @@ -82,3 +88,165 @@ async fn can_handle_blobs() -> eyre::Result<()> { Ok(()) } + +#[tokio::test] +async fn can_send_legacy_sidecar_post_activation() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + let tasks = TaskManager::current(); + let exec = tasks.executor(); + + let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json")).unwrap(); + let chain_spec = Arc::new( + ChainSpecBuilder::default().chain(MAINNET.chain).genesis(genesis).osaka_activated().build(), + ); + let genesis_hash = chain_spec.genesis_hash(); + let node_config = NodeConfig::test() + .with_chain(chain_spec) + .with_unused_ports() + .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()); + let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone()) + .testing_node(exec.clone()) + .node(EthereumNode::default()) + .launch() + .await?; + + let mut node = NodeTestContext::new(node, eth_payload_attributes).await?; + + let wallets = Wallet::new(2).wallet_gen(); + let blob_wallet = wallets.first().unwrap(); + + // build blob tx + let blob_tx = TransactionTestContext::tx_with_blobs_bytes(1, blob_wallet.clone()).await?; + + let tx = PooledTransactionVariant::decode_2718_exact(&blob_tx).unwrap(); + assert!(tx.as_eip4844().unwrap().tx().sidecar.is_eip4844()); + + // inject blob tx to the pool + let blob_tx_hash = node.rpc.inject_tx(blob_tx).await?; + // fetch it from rpc + let envelope = node.rpc.envelope_by_hash(blob_tx_hash).await?; + // assert that sidecar was converted to eip7594 + assert!(envelope.as_eip4844().unwrap().tx().sidecar().unwrap().is_eip7594()); + // validate sidecar + TransactionTestContext::validate_sidecar(envelope); + + // build a payload + let blob_payload = node.new_payload().await?; + + // submit the blob payload + let blob_block_hash = node.submit_payload(blob_payload).await?; + + node.update_forkchoice(genesis_hash, blob_block_hash).await?; + + Ok(()) +} + +#[tokio::test] +async fn blob_conversion_at_osaka() -> eyre::Result<()> { + reth_tracing::init_test_tracing(); + let tasks = TaskManager::current(); + let exec = tasks.executor(); + + let current_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs(); + // Osaka activates in 2 slots + let osaka_timestamp = current_timestamp + 24; + + let genesis: Genesis = serde_json::from_str(include_str!("../assets/genesis.json")).unwrap(); + let chain_spec = Arc::new( + ChainSpecBuilder::default() + .chain(MAINNET.chain) + .genesis(genesis) + .prague_activated() + .with_osaka_at(osaka_timestamp) + .build(), + ); + let genesis_hash = chain_spec.genesis_hash(); + let node_config = NodeConfig::test() + .with_chain(chain_spec) + .with_unused_ports() + .with_rpc(RpcServerArgs::default().with_unused_ports().with_http()); + let NodeHandle { node, node_exit_future: _ } = NodeBuilder::new(node_config.clone()) + .testing_node(exec.clone()) + .node(EthereumNode::default()) + .launch() + .await?; + + let mut node = NodeTestContext::new(node, eth_payload_attributes).await?; + + let mut wallets = Wallet::new(3).wallet_gen(); + let first = wallets.pop().unwrap(); + let second = wallets.pop().unwrap(); + + // build a dummy payload at `current_timestamp` + let raw_tx = TransactionTestContext::transfer_tx_bytes(1, wallets.pop().unwrap()).await; + node.rpc.inject_tx(raw_tx).await?; + node.payload.timestamp = current_timestamp - 1; + node.advance_block().await?; + + // build blob txs + let first_blob = TransactionTestContext::tx_with_blobs_bytes(1, first.clone()).await?; + let second_blob = TransactionTestContext::tx_with_blobs_bytes(1, second.clone()).await?; + + // assert both txs have legacy sidecars + assert!(PooledTransactionVariant::decode_2718_exact(&first_blob) + .unwrap() + .as_eip4844() + .unwrap() + .tx() + .sidecar + .is_eip4844()); + assert!(PooledTransactionVariant::decode_2718_exact(&second_blob) + .unwrap() + .as_eip4844() + .unwrap() + .tx() + .sidecar + .is_eip4844()); + + // inject first blob tx to the pool + let blob_tx_hash = node.rpc.inject_tx(first_blob).await?; + // fetch it from rpc + let envelope = node.rpc.envelope_by_hash(blob_tx_hash).await?; + // assert that it still has a legacy sidecar + assert!(envelope.as_eip4844().unwrap().tx().sidecar().unwrap().is_eip4844()); + // validate sidecar + TransactionTestContext::validate_sidecar(envelope); + + // build last Prague payload + node.payload.timestamp = current_timestamp + 11; + let prague_payload = node.new_payload().await?; + assert!(matches!(prague_payload.sidecars(), BlobSidecars::Eip4844(_))); + + // inject second blob tx to the pool + let blob_tx_hash = node.rpc.inject_tx(second_blob).await?; + // fetch it from rpc + let envelope = node.rpc.envelope_by_hash(blob_tx_hash).await?; + // assert that it still has a legacy sidecar + assert!(envelope.as_eip4844().unwrap().tx().sidecar().unwrap().is_eip4844()); + // validate sidecar + TransactionTestContext::validate_sidecar(envelope); + + tokio::time::sleep(Duration::from_secs(11)).await; + + // fetch second blob tx from rpc again + let envelope = node.rpc.envelope_by_hash(blob_tx_hash).await?; + // assert that it was converted to eip7594 + assert!(envelope.as_eip4844().unwrap().tx().sidecar().unwrap().is_eip7594()); + // validate sidecar + TransactionTestContext::validate_sidecar(envelope); + + // submit the Prague payload + node.update_forkchoice(genesis_hash, node.submit_payload(prague_payload).await?).await?; + + // Build first Osaka payload + node.payload.timestamp = osaka_timestamp - 1; + let osaka_payload = node.new_payload().await?; + + // Assert that it includes the second blob tx with eip7594 sidecar + assert!(osaka_payload.block().body().transactions().any(|tx| *tx.hash() == blob_tx_hash)); + assert!(matches!(osaka_payload.sidecars(), BlobSidecars::Eip7594(_))); + + node.update_forkchoice(genesis_hash, node.submit_payload(osaka_payload).await?).await?; + + Ok(()) +} diff --git a/crates/ethereum/payload/src/lib.rs b/crates/ethereum/payload/src/lib.rs index 8c969c9d44c..7f40e983bc8 100644 --- a/crates/ethereum/payload/src/lib.rs +++ b/crates/ethereum/payload/src/lib.rs @@ -176,8 +176,8 @@ where debug!(target: "payload_builder", id=%attributes.id, parent_header = ?parent_header.hash(), parent_number = parent_header.number, "building new payload"); let mut cumulative_gas_used = 0; - let block_gas_limit: u64 = builder.evm_mut().block().gas_limit; - let base_fee = builder.evm_mut().block().basefee; + let block_gas_limit: u64 = builder.evm_mut().block().gas_limit(); + let base_fee = builder.evm_mut().block().basefee(); let mut best_txs = best_txs(BestTransactionsAttributes::new( base_fee, diff --git a/crates/evm/evm/src/aliases.rs b/crates/evm/evm/src/aliases.rs index 6bb1ab1c35a..7758f0aea17 100644 --- a/crates/evm/evm/src/aliases.rs +++ b/crates/evm/evm/src/aliases.rs @@ -11,6 +11,9 @@ pub type EvmFactoryFor = /// Helper to access [`EvmFactory::Spec`] for a given [`ConfigureEvm`]. pub type SpecFor = as EvmFactory>::Spec; +/// Helper to access [`EvmFactory::BlockEnv`] for a given [`ConfigureEvm`]. +pub type BlockEnvFor = as EvmFactory>::BlockEnv; + /// Helper to access [`EvmFactory::Evm`] for a given [`ConfigureEvm`]. pub type EvmFor = as EvmFactory>::Evm; @@ -31,7 +34,7 @@ pub type ExecutionCtxFor<'a, Evm> = <::BlockExecutorFactory as BlockExecutorFactory>::ExecutionCtx<'a>; /// Type alias for [`EvmEnv`] for a given [`ConfigureEvm`]. -pub type EvmEnvFor = EvmEnv>; +pub type EvmEnvFor = EvmEnv, BlockEnvFor>; /// Helper trait to bound [`Inspector`] for a [`ConfigureEvm`]. pub trait InspectorFor: Inspector> {} diff --git a/crates/evm/evm/src/engine.rs b/crates/evm/evm/src/engine.rs index 5c721d811bc..5b46a086170 100644 --- a/crates/evm/evm/src/engine.rs +++ b/crates/evm/evm/src/engine.rs @@ -2,7 +2,7 @@ use crate::{execute::ExecutableTxFor, ConfigureEvm, EvmEnvFor, ExecutionCtxFor}; /// [`ConfigureEvm`] extension providing methods for executing payloads. pub trait ConfigureEngineEvm: ConfigureEvm { - /// Returns an [`EvmEnvFor`] for the given payload. + /// Returns an [`crate::EvmEnv`] for the given payload. fn evm_env_for_payload(&self, payload: &ExecutionData) -> Result, Self::Error>; /// Returns an [`ExecutionCtxFor`] for the given payload. diff --git a/crates/evm/evm/src/execute.rs b/crates/evm/evm/src/execute.rs index 28b972e7c95..76a9b078394 100644 --- a/crates/evm/evm/src/execute.rs +++ b/crates/evm/evm/src/execute.rs @@ -203,7 +203,8 @@ pub struct BlockAssemblerInput<'a, 'b, F: BlockExecutorFactory, H = Header> { /// Configuration of EVM used when executing the block. /// /// Contains context relevant to EVM such as [`revm::context::BlockEnv`]. - pub evm_env: EvmEnv<::Spec>, + pub evm_env: + EvmEnv<::Spec, ::BlockEnv>, /// [`BlockExecutorFactory::ExecutionCtx`] used to execute the block. pub execution_ctx: F::ExecutionCtx<'a>, /// Parent block header. @@ -225,7 +226,10 @@ impl<'a, 'b, F: BlockExecutorFactory, H> BlockAssemblerInput<'a, 'b, F, H> { /// Creates a new [`BlockAssemblerInput`]. #[expect(clippy::too_many_arguments)] pub fn new( - evm_env: EvmEnv<::Spec>, + evm_env: EvmEnv< + ::Spec, + ::BlockEnv, + >, execution_ctx: F::ExecutionCtx<'a>, parent: &'a SealedHeader, transactions: Vec, @@ -465,6 +469,7 @@ where Evm: Evm< Spec = ::Spec, HaltReason = ::HaltReason, + BlockEnv = ::BlockEnv, DB = &'a mut State, >, Transaction = N::SignedTx, diff --git a/crates/evm/evm/src/lib.rs b/crates/evm/evm/src/lib.rs index a2a30b9e0ab..e2101fd915b 100644 --- a/crates/evm/evm/src/lib.rs +++ b/crates/evm/evm/src/lib.rs @@ -404,7 +404,13 @@ pub trait ConfigureEvm: Clone + Debug + Send + Sync + Unpin { db: &'a mut State, parent: &'a SealedHeader<::BlockHeader>, attributes: Self::NextBlockEnvCtx, - ) -> Result, Self::Error> { + ) -> Result< + impl BlockBuilder< + Primitives = Self::Primitives, + Executor: BlockExecutorFor<'a, Self::BlockExecutorFactory, DB>, + >, + Self::Error, + > { let evm_env = self.next_evm_env(parent, &attributes)?; let evm = self.evm_with_env(db, evm_env); let ctx = self.context_for_next_block(parent, attributes)?; diff --git a/crates/exex/exex/src/backfill/factory.rs b/crates/exex/exex/src/backfill/factory.rs index 789d63f84e2..d9a51bc47a7 100644 --- a/crates/exex/exex/src/backfill/factory.rs +++ b/crates/exex/exex/src/backfill/factory.rs @@ -24,7 +24,7 @@ impl BackfillJobFactory { Self { evm_config, provider, - prune_modes: PruneModes::none(), + prune_modes: PruneModes::default(), thresholds: ExecutionStageThresholds { // Default duration for a database transaction to be considered long-lived is // 60 seconds, so we limit the backfill job to the half of it to be sure we finish diff --git a/crates/fs-util/src/lib.rs b/crates/fs-util/src/lib.rs index d3195ad27fe..08817aecfa3 100644 --- a/crates/fs-util/src/lib.rs +++ b/crates/fs-util/src/lib.rs @@ -332,10 +332,7 @@ where Err(err) => { // Clean up the temporary file before returning the error let _ = fs::remove_file(&tmp_path); - return Err(FsPathError::Write { - source: Error::other(err.into()), - path: tmp_path.clone(), - }); + return Err(FsPathError::Write { source: Error::other(err.into()), path: tmp_path }); } } diff --git a/crates/net/discv4/src/lib.rs b/crates/net/discv4/src/lib.rs index 3686d7bf690..83106cbbe6e 100644 --- a/crates/net/discv4/src/lib.rs +++ b/crates/net/discv4/src/lib.rs @@ -2402,7 +2402,7 @@ pub enum DiscoveryUpdate { /// Node that was removed from the table Removed(PeerId), /// A series of updates - Batch(Vec), + Batch(Vec), } #[cfg(test)] diff --git a/crates/net/discv5/src/error.rs b/crates/net/discv5/src/error.rs index c373a17194c..64b2cd73af8 100644 --- a/crates/net/discv5/src/error.rs +++ b/crates/net/discv5/src/error.rs @@ -13,7 +13,7 @@ pub enum Error { #[error("network stack identifier is not configured")] NetworkStackIdNotConfigured, /// Missing key used to identify rlpx network. - #[error("fork missing on enr, key missing")] + #[error("fork missing on enr, key {0:?} and key 'eth' missing")] ForkMissing(&'static [u8]), /// Failed to decode [`ForkId`](reth_ethereum_forks::ForkId) rlp value. #[error("failed to decode fork id, 'eth': {0:?}")] diff --git a/crates/net/discv5/src/lib.rs b/crates/net/discv5/src/lib.rs index be7b781fe74..ef2c69caedb 100644 --- a/crates/net/discv5/src/lib.rs +++ b/crates/net/discv5/src/lib.rs @@ -320,10 +320,7 @@ impl Discv5 { return None } - // todo: extend for all network stacks in reth-network rlpx logic - let fork_id = (self.fork_key == Some(NetworkStackId::ETH)) - .then(|| self.get_fork_id(enr).ok()) - .flatten(); + let fork_id = self.get_fork_id(enr).ok(); trace!(target: "net::discv5", ?fork_id, @@ -387,7 +384,22 @@ impl Discv5 { let Some(key) = self.fork_key else { return Err(Error::NetworkStackIdNotConfigured) }; let fork_id = enr .get_decodable::(key) - .ok_or(Error::ForkMissing(key))? + .or_else(|| { + (key != NetworkStackId::ETH) + .then(|| { + // Fallback: trying to get fork id from Enr with 'eth' as network stack id + trace!(target: "net::discv5", + key = %String::from_utf8_lossy(key), + "Fork id not found for key, trying 'eth'..." + ); + enr.get_decodable::(NetworkStackId::ETH) + }) + .flatten() + }) + .ok_or({ + trace!(target: "net::discv5", "Fork id not found for 'eth' network stack id"); + Error::ForkMissing(key) + })? .map(Into::into)?; Ok(fork_id) @@ -669,6 +681,8 @@ mod test { use ::enr::{CombinedKey, EnrKey}; use rand_08::thread_rng; use reth_chainspec::MAINNET; + use reth_tracing::init_test_tracing; + use std::env; use tracing::trace; fn discv5_noop() -> Discv5 { @@ -901,4 +915,55 @@ mod test { assert_eq!(fork_id, decoded_fork_id); assert_eq!(TCP_PORT, enr.tcp4().unwrap()); // listen config is defaulting to ip mode ipv4 } + + #[test] + fn get_fork_id_with_different_network_stack_ids() { + unsafe { + env::set_var("RUST_LOG", "net::discv5=trace"); + } + init_test_tracing(); + + let fork_id = MAINNET.latest_fork_id(); + let sk = SecretKey::new(&mut thread_rng()); + + // Test 1: ENR with OPEL fork ID, Discv5 configured for OPEL + let enr_with_opel = Enr::builder() + .add_value_rlp( + NetworkStackId::OPEL, + alloy_rlp::encode(EnrForkIdEntry::from(fork_id)).into(), + ) + .build(&sk) + .unwrap(); + + let mut discv5 = discv5_noop(); + discv5.fork_key = Some(NetworkStackId::OPEL); + assert_eq!(discv5.get_fork_id(&enr_with_opel).unwrap(), fork_id); + + // Test 2: ENR with ETH fork ID, Discv5 configured for OPEL (fallback to ETH) + let enr_with_eth = Enr::builder() + .add_value_rlp( + NetworkStackId::ETH, + alloy_rlp::encode(EnrForkIdEntry::from(fork_id)).into(), + ) + .build(&sk) + .unwrap(); + + discv5.fork_key = Some(NetworkStackId::OPEL); + assert_eq!(discv5.get_fork_id(&enr_with_eth).unwrap(), fork_id); + + // Test 3: ENR with neither OPEL nor ETH fork ID (should fail) + let enr_without_network_stack_id = Enr::empty(&sk).unwrap(); + discv5.fork_key = Some(NetworkStackId::OPEL); + assert!(matches!( + discv5.get_fork_id(&enr_without_network_stack_id), + Err(Error::ForkMissing(NetworkStackId::OPEL)) + )); + + // Test 4: discv5 without network stack id configured (should fail) + let discv5 = discv5_noop(); + assert!(matches!( + discv5.get_fork_id(&enr_without_network_stack_id), + Err(Error::NetworkStackIdNotConfigured) + )); + } } diff --git a/crates/net/downloaders/src/bodies/bodies.rs b/crates/net/downloaders/src/bodies/bodies.rs index 09eb22854d4..153f269fe41 100644 --- a/crates/net/downloaders/src/bodies/bodies.rs +++ b/crates/net/downloaders/src/bodies/bodies.rs @@ -347,6 +347,12 @@ where // written by external services (e.g. BlockchainTree). tracing::trace!(target: "downloaders::bodies", ?range, prev_range = ?self.download_range, "Download range reset"); info!(target: "downloaders::bodies", count, ?range, "Downloading bodies"); + // Increment out-of-order requests metric if the new start is below the last returned block + if let Some(last_returned) = self.latest_queued_block_number && + *range.start() < last_returned + { + self.metrics.out_of_order_requests.increment(1); + } self.clear(); self.download_range = range; Ok(()) diff --git a/crates/net/ecies/src/algorithm.rs b/crates/net/ecies/src/algorithm.rs index 350cd3f7ed4..dae5e501695 100644 --- a/crates/net/ecies/src/algorithm.rs +++ b/crates/net/ecies/src/algorithm.rs @@ -499,7 +499,7 @@ impl ECIES { } /// Read and verify an auth message from the input data. - #[tracing::instrument(skip_all)] + #[tracing::instrument(level = "trace", skip_all)] pub fn read_auth(&mut self, data: &mut [u8]) -> Result<(), ECIESError> { self.remote_init_msg = Some(Bytes::copy_from_slice(data)); let unencrypted = self.decrypt_message(data)?; @@ -571,7 +571,7 @@ impl ECIES { } /// Read and verify an ack message from the input data. - #[tracing::instrument(skip_all)] + #[tracing::instrument(level = "trace", skip_all)] pub fn read_ack(&mut self, data: &mut [u8]) -> Result<(), ECIESError> { self.remote_init_msg = Some(Bytes::copy_from_slice(data)); let unencrypted = self.decrypt_message(data)?; diff --git a/crates/net/ecies/src/codec.rs b/crates/net/ecies/src/codec.rs index b5a10284cf2..c4c45366c66 100644 --- a/crates/net/ecies/src/codec.rs +++ b/crates/net/ecies/src/codec.rs @@ -58,7 +58,7 @@ impl Decoder for ECIESCodec { type Item = IngressECIESValue; type Error = ECIESError; - #[instrument(level = "trace", skip_all, fields(peer=?self.ecies.remote_id, state=?self.state))] + #[instrument(skip_all, fields(peer=?self.ecies.remote_id, state=?self.state))] fn decode(&mut self, buf: &mut BytesMut) -> Result, Self::Error> { loop { match self.state { @@ -150,7 +150,7 @@ impl Decoder for ECIESCodec { impl Encoder for ECIESCodec { type Error = io::Error; - #[instrument(level = "trace", skip(self, buf), fields(peer=?self.ecies.remote_id, state=?self.state))] + #[instrument(skip(self, buf), fields(peer=?self.ecies.remote_id, state=?self.state))] fn encode(&mut self, item: EgressECIESValue, buf: &mut BytesMut) -> Result<(), Self::Error> { match item { EgressECIESValue::Auth => { diff --git a/crates/net/ecies/src/error.rs b/crates/net/ecies/src/error.rs index 9dabfc16183..a93b731fee6 100644 --- a/crates/net/ecies/src/error.rs +++ b/crates/net/ecies/src/error.rs @@ -33,7 +33,7 @@ pub enum ECIESErrorImpl { #[error(transparent)] IO(std::io::Error), /// Error when checking the HMAC tag against the tag on the message being decrypted - #[error("tag check failure in read_header")] + #[error("tag check failure in decrypt_message")] TagCheckDecryptFailed, /// Error when checking the HMAC tag against the tag on the header #[error("tag check failure in read_header")] @@ -47,8 +47,8 @@ pub enum ECIESErrorImpl { /// Error when parsing ACK data #[error("invalid ack data")] InvalidAckData, - /// Error when reading the header if its length is <3 - #[error("invalid body data")] + /// Error when reading/parsing the `RLPx` header + #[error("invalid header")] InvalidHeader, /// Error when interacting with secp256k1 #[error(transparent)] diff --git a/crates/net/ecies/src/stream.rs b/crates/net/ecies/src/stream.rs index 9915fc42e6a..830f3f5ddef 100644 --- a/crates/net/ecies/src/stream.rs +++ b/crates/net/ecies/src/stream.rs @@ -40,7 +40,7 @@ where Io: AsyncRead + AsyncWrite + Unpin, { /// Connect to an `ECIES` server - #[instrument(skip(transport, secret_key))] + #[instrument(level = "trace", skip(transport, secret_key))] pub async fn connect( transport: Io, secret_key: SecretKey, diff --git a/crates/net/network/src/session/counter.rs b/crates/net/network/src/session/counter.rs index db9bd16cda9..a3318ea05c5 100644 --- a/crates/net/network/src/session/counter.rs +++ b/crates/net/network/src/session/counter.rs @@ -3,7 +3,7 @@ use reth_network_api::Direction; use reth_network_types::SessionLimits; /// Keeps track of all sessions. -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct SessionCounter { /// Limits to enforce. limits: SessionLimits, diff --git a/crates/net/network/src/transactions/fetcher.rs b/crates/net/network/src/transactions/fetcher.rs index 1cb725e4efb..a112e8cac89 100644 --- a/crates/net/network/src/transactions/fetcher.rs +++ b/crates/net/network/src/transactions/fetcher.rs @@ -284,9 +284,7 @@ impl TransactionFetcher { // folds size based on expected response size and adds selected hashes to the request // list and the other hashes to the surplus list - loop { - let Some((hash, metadata)) = hashes_from_announcement_iter.next() else { break }; - + for (hash, metadata) in hashes_from_announcement_iter.by_ref() { let Some((_ty, size)) = metadata else { unreachable!("this method is called upon reception of an eth68 announcement") }; @@ -413,7 +411,6 @@ impl TransactionFetcher { if let (_, Some(evicted_hash)) = self.hashes_pending_fetch.insert_and_get_evicted(hash) { self.hashes_fetch_inflight_and_pending_fetch.remove(&evicted_hash); - self.hashes_pending_fetch.remove(&evicted_hash); } } } diff --git a/crates/node/builder/src/components/pool.rs b/crates/node/builder/src/components/pool.rs index 9be184bc9c0..a261f02c756 100644 --- a/crates/node/builder/src/components/pool.rs +++ b/crates/node/builder/src/components/pool.rs @@ -3,7 +3,8 @@ use crate::{BuilderContext, FullNodeTypes}; use alloy_primitives::Address; use reth_chain_state::CanonStateSubscriptions; -use reth_node_api::TxTy; +use reth_chainspec::EthereumHardforks; +use reth_node_api::{NodeTypes, TxTy}; use reth_transaction_pool::{ blobstore::DiskFileBlobStore, CoinbaseTipOrdering, PoolConfig, PoolTransaction, SubPoolLimit, TransactionPool, TransactionValidationTaskExecutor, TransactionValidator, @@ -125,8 +126,9 @@ impl<'a, Node: FullNodeTypes, V> TxPoolBuilder<'a, Node, V> { } } -impl<'a, Node: FullNodeTypes, V> TxPoolBuilder<'a, Node, TransactionValidationTaskExecutor> +impl<'a, Node, V> TxPoolBuilder<'a, Node, TransactionValidationTaskExecutor> where + Node: FullNodeTypes>, V: TransactionValidator + 'static, V::Transaction: PoolTransaction> + reth_transaction_pool::EthPoolTransaction, @@ -227,7 +229,7 @@ fn spawn_pool_maintenance_task( pool_config: &PoolConfig, ) -> eyre::Result<()> where - Node: FullNodeTypes, + Node: FullNodeTypes>, Pool: reth_transaction_pool::TransactionPoolExt + Clone + 'static, Pool::Transaction: PoolTransaction>, { @@ -259,7 +261,7 @@ pub fn spawn_maintenance_tasks( pool_config: &PoolConfig, ) -> eyre::Result<()> where - Node: FullNodeTypes, + Node: FullNodeTypes>, Pool: reth_transaction_pool::TransactionPoolExt + Clone + 'static, Pool::Transaction: PoolTransaction>, { diff --git a/crates/node/builder/src/launch/engine.rs b/crates/node/builder/src/launch/engine.rs index 02fb505b077..3b43f5f3299 100644 --- a/crates/node/builder/src/launch/engine.rs +++ b/crates/node/builder/src/launch/engine.rs @@ -236,6 +236,7 @@ impl EngineNodeLauncher { info!(target: "reth::cli", "Consensus engine initialized"); + #[allow(clippy::needless_continue)] let events = stream_select!( event_sender.new_listener().map(Into::into), pipeline_events.map(Into::into), diff --git a/crates/node/builder/src/rpc.rs b/crates/node/builder/src/rpc.rs index 70adcc83d69..ed0a3fb64d4 100644 --- a/crates/node/builder/src/rpc.rs +++ b/crates/node/builder/src/rpc.rs @@ -23,8 +23,8 @@ use reth_node_core::{ version::{version_metadata, CLIENT_CODE}, }; use reth_payload_builder::{PayloadBuilderHandle, PayloadStore}; -use reth_rpc::eth::{core::EthRpcConverterFor, EthApiTypes, FullEthApiServer}; -use reth_rpc_api::{eth::helpers::AddDevSigners, IntoEngineApiRpcModule}; +use reth_rpc::eth::{core::EthRpcConverterFor, DevSigner, EthApiTypes, FullEthApiServer}; +use reth_rpc_api::{eth::helpers::EthTransactions, IntoEngineApiRpcModule}; use reth_rpc_builder::{ auth::{AuthRpcModule, AuthServerHandle}, config::RethRpcServerConfig, @@ -991,7 +991,8 @@ where // in dev mode we generate 20 random dev-signer accounts if config.dev.dev { - registry.eth_api().with_dev_accounts(); + let signers = DevSigner::from_mnemonic(config.dev.dev_mnemonic.as_str(), 20); + registry.eth_api().signers().write().extend(signers); } let mut registry = RpcRegistry { registry }; @@ -1163,7 +1164,6 @@ pub trait EthApiBuilder: Default + Send + 'static { /// The Ethapi implementation this builder will build. type EthApi: EthApiTypes + FullEthApiServer - + AddDevSigners + Unpin + 'static; diff --git a/crates/node/core/Cargo.toml b/crates/node/core/Cargo.toml index bf784b50703..4d4fd475ac4 100644 --- a/crates/node/core/Cargo.toml +++ b/crates/node/core/Cargo.toml @@ -45,7 +45,7 @@ alloy-eips.workspace = true # misc eyre.workspace = true -clap = { workspace = true, features = ["derive"] } +clap = { workspace = true, features = ["derive", "env"] } humantime.workspace = true rand.workspace = true derive_more.workspace = true diff --git a/crates/node/core/src/args/dev.rs b/crates/node/core/src/args/dev.rs index b6a01745257..d62ff1c5dce 100644 --- a/crates/node/core/src/args/dev.rs +++ b/crates/node/core/src/args/dev.rs @@ -5,8 +5,10 @@ use std::time::Duration; use clap::Args; use humantime::parse_duration; +const DEFAULT_MNEMONIC: &str = "test test test test test test test test test test test junk"; + /// Parameters for Dev testnet configuration -#[derive(Debug, Args, PartialEq, Eq, Default, Clone, Copy)] +#[derive(Debug, Args, PartialEq, Eq, Clone)] #[command(next_help_heading = "Dev testnet")] pub struct DevArgs { /// Start the node in dev mode @@ -39,6 +41,28 @@ pub struct DevArgs { verbatim_doc_comment )] pub block_time: Option, + + /// Derive dev accounts from a fixed mnemonic instead of random ones. + #[arg( + long = "dev.mnemonic", + help_heading = "Dev testnet", + value_name = "MNEMONIC", + requires = "dev", + verbatim_doc_comment, + default_value = DEFAULT_MNEMONIC + )] + pub dev_mnemonic: String, +} + +impl Default for DevArgs { + fn default() -> Self { + Self { + dev: false, + block_max_transactions: None, + block_time: None, + dev_mnemonic: DEFAULT_MNEMONIC.to_string(), + } + } } #[cfg(test)] @@ -56,13 +80,37 @@ mod tests { #[test] fn test_parse_dev_args() { let args = CommandParser::::parse_from(["reth"]).args; - assert_eq!(args, DevArgs { dev: false, block_max_transactions: None, block_time: None }); + assert_eq!( + args, + DevArgs { + dev: false, + block_max_transactions: None, + block_time: None, + dev_mnemonic: DEFAULT_MNEMONIC.to_string(), + } + ); let args = CommandParser::::parse_from(["reth", "--dev"]).args; - assert_eq!(args, DevArgs { dev: true, block_max_transactions: None, block_time: None }); + assert_eq!( + args, + DevArgs { + dev: true, + block_max_transactions: None, + block_time: None, + dev_mnemonic: DEFAULT_MNEMONIC.to_string(), + } + ); let args = CommandParser::::parse_from(["reth", "--auto-mine"]).args; - assert_eq!(args, DevArgs { dev: true, block_max_transactions: None, block_time: None }); + assert_eq!( + args, + DevArgs { + dev: true, + block_max_transactions: None, + block_time: None, + dev_mnemonic: DEFAULT_MNEMONIC.to_string(), + } + ); let args = CommandParser::::parse_from([ "reth", @@ -71,7 +119,15 @@ mod tests { "2", ]) .args; - assert_eq!(args, DevArgs { dev: true, block_max_transactions: Some(2), block_time: None }); + assert_eq!( + args, + DevArgs { + dev: true, + block_max_transactions: Some(2), + block_time: None, + dev_mnemonic: DEFAULT_MNEMONIC.to_string(), + } + ); let args = CommandParser::::parse_from(["reth", "--dev", "--dev.block-time", "1s"]).args; @@ -80,7 +136,8 @@ mod tests { DevArgs { dev: true, block_max_transactions: None, - block_time: Some(std::time::Duration::from_secs(1)) + block_time: Some(std::time::Duration::from_secs(1)), + dev_mnemonic: DEFAULT_MNEMONIC.to_string(), } ); } diff --git a/crates/node/core/src/args/engine.rs b/crates/node/core/src/args/engine.rs index 6b678b5789b..c82b1b03a15 100644 --- a/crates/node/core/src/args/engine.rs +++ b/crates/node/core/src/args/engine.rs @@ -30,9 +30,9 @@ pub struct EngineArgs { #[deprecated] pub caching_and_prewarming_enabled: bool, - /// Disable cross-block caching and parallel prewarming - #[arg(long = "engine.disable-caching-and-prewarming")] - pub caching_and_prewarming_disabled: bool, + /// Disable parallel prewarming + #[arg(long = "engine.disable-prewarming", alias = "engine.disable-caching-and-prewarming")] + pub prewarming_disabled: bool, /// CAUTION: This CLI flag has no effect anymore, use --engine.disable-parallel-sparse-trie /// if you want to disable usage of the `ParallelSparseTrie`. @@ -129,7 +129,7 @@ impl Default for EngineArgs { legacy_state_root_task_enabled: false, state_root_task_compare_updates: false, caching_and_prewarming_enabled: true, - caching_and_prewarming_disabled: false, + prewarming_disabled: false, parallel_sparse_trie_enabled: true, parallel_sparse_trie_disabled: false, state_provider_metrics: false, @@ -157,7 +157,7 @@ impl EngineArgs { .with_persistence_threshold(self.persistence_threshold) .with_memory_block_buffer_target(self.memory_block_buffer_target) .with_legacy_state_root(self.legacy_state_root_task_enabled) - .without_caching_and_prewarming(self.caching_and_prewarming_disabled) + .without_prewarming(self.prewarming_disabled) .with_disable_parallel_sparse_trie(self.parallel_sparse_trie_disabled) .with_state_provider_metrics(self.state_provider_metrics) .with_always_compare_trie_updates(self.state_root_task_compare_updates) diff --git a/crates/node/core/src/args/log.rs b/crates/node/core/src/args/log.rs index 99fefc11445..20c60362d7b 100644 --- a/crates/node/core/src/args/log.rs +++ b/crates/node/core/src/args/log.rs @@ -139,7 +139,7 @@ impl LogArgs { pub enum ColorMode { /// Colors on Always, - /// Colors on + /// Auto-detect Auto, /// Colors off Never, diff --git a/crates/node/core/src/args/pruning.rs b/crates/node/core/src/args/pruning.rs index e96245350fd..846e4e6b203 100644 --- a/crates/node/core/src/args/pruning.rs +++ b/crates/node/core/src/args/pruning.rs @@ -126,6 +126,7 @@ impl PruningArgs { storage_history: Some(PruneMode::Distance(MINIMUM_PRUNING_DISTANCE)), // TODO: set default to pre-merge block if available bodies_history: None, + merkle_changesets: PruneMode::Distance(MINIMUM_PRUNING_DISTANCE), receipts_log_filter: Default::default(), }, } diff --git a/crates/node/core/src/args/stage.rs b/crates/node/core/src/args/stage.rs index 337f5a4a60b..7718fb85605 100644 --- a/crates/node/core/src/args/stage.rs +++ b/crates/node/core/src/args/stage.rs @@ -38,6 +38,11 @@ pub enum StageEnum { /// /// Handles Merkle tree-related computations and data processing. Merkle, + /// The merkle changesets stage within the pipeline. + /// + /// Handles Merkle trie changesets for storage and accounts. + #[value(name = "merkle-changesets")] + MerkleChangeSets, /// The transaction lookup stage within the pipeline. /// /// Deals with the retrieval and processing of transactions. diff --git a/crates/node/core/src/args/trace.rs b/crates/node/core/src/args/trace.rs index 751ab556ac8..2e37feb6739 100644 --- a/crates/node/core/src/args/trace.rs +++ b/crates/node/core/src/args/trace.rs @@ -2,19 +2,22 @@ use clap::Parser; use eyre::{ensure, WrapErr}; -use tracing::Level; +use reth_tracing::tracing_subscriber::EnvFilter; use url::Url; /// CLI arguments for configuring `Opentelemetry` trace and span export. #[derive(Debug, Clone, Parser)] pub struct TraceArgs { - /// Enable `Opentelemetry` tracing export to an OTLP endpoint. + /// Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently + /// only http exporting is supported. /// /// If no value provided, defaults to `http://localhost:4318/v1/traces`. /// /// Example: --tracing-otlp=http://collector:4318/v1/traces #[arg( long = "tracing-otlp", + // Per specification. + env = "OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", global = true, value_name = "URL", num_args = 0..=1, @@ -25,30 +28,37 @@ pub struct TraceArgs { )] pub otlp: Option, - /// Set the minimum log level for OTLP traces. + /// 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. /// - /// Valid values: ERROR, WARN, INFO, DEBUG, TRACE + /// Example: --tracing-otlp.filter=info,reth=debug,hyper_util=off /// /// Defaults to TRACE if not specified. #[arg( - long = "tracing-otlp-level", + long = "tracing-otlp.filter", global = true, - value_name = "LEVEL", + value_name = "FILTER", default_value = "TRACE", help_heading = "Tracing" )] - pub otlp_level: Level, + pub otlp_filter: EnvFilter, } impl Default for TraceArgs { fn default() -> Self { - Self { otlp: None, otlp_level: Level::TRACE } + Self { otlp: None, otlp_filter: EnvFilter::from_default_env() } } } // Parses and validates an OTLP endpoint url. fn parse_otlp_endpoint(arg: &str) -> eyre::Result { - let url = Url::parse(arg).wrap_err("Invalid URL for OTLP trace output")?; + let mut url = Url::parse(arg).wrap_err("Invalid URL for OTLP trace output")?; + + // If the path is empty, we set the path. + if url.path() == "/" { + url.set_path("/v1/traces") + } // OTLP url must end with `/v1/traces` per the OTLP specification. ensure!( diff --git a/crates/node/core/src/node_config.rs b/crates/node/core/src/node_config.rs index 94dbecb649c..7b487a1fa71 100644 --- a/crates/node/core/src/node_config.rs +++ b/crates/node/core/src/node_config.rs @@ -272,7 +272,7 @@ impl NodeConfig { } /// Set the dev args for the node - pub const fn with_dev(mut self, dev: DevArgs) -> Self { + pub fn with_dev(mut self, dev: DevArgs) -> Self { self.dev = dev; self } @@ -519,7 +519,7 @@ impl Clone for NodeConfig { builder: self.builder.clone(), debug: self.debug.clone(), db: self.db, - dev: self.dev, + dev: self.dev.clone(), pruning: self.pruning.clone(), datadir: self.datadir.clone(), engine: self.engine.clone(), diff --git a/crates/optimism/cli/src/app.rs b/crates/optimism/cli/src/app.rs index 891578cbe24..621d16c7e13 100644 --- a/crates/optimism/cli/src/app.rs +++ b/crates/optimism/cli/src/app.rs @@ -124,7 +124,7 @@ where layers.with_span_layer( "reth".to_string(), output_type.clone(), - self.cli.traces.otlp_level, + self.cli.traces.otlp_filter.clone(), )?; } diff --git a/crates/optimism/cli/src/commands/import.rs b/crates/optimism/cli/src/commands/import.rs index 0fd1d64ac12..74656511af1 100644 --- a/crates/optimism/cli/src/commands/import.rs +++ b/crates/optimism/cli/src/commands/import.rs @@ -71,6 +71,9 @@ impl> ImportOpCommand { .sealed_header(provider_factory.last_block_number()?)? .expect("should have genesis"); + let static_file_producer = + StaticFileProducer::new(provider_factory.clone(), PruneModes::default()); + while let Some(mut file_client) = reader.next_chunk::>(consensus.clone(), Some(sealed_header)).await? { @@ -100,7 +103,7 @@ impl> ImportOpCommand { provider_factory.clone(), &consensus, Arc::new(file_client), - StaticFileProducer::new(provider_factory.clone(), PruneModes::default()), + static_file_producer.clone(), true, OpExecutorProvider::optimism(provider_factory.chain_spec()), )?; diff --git a/crates/optimism/cli/src/commands/init_state.rs b/crates/optimism/cli/src/commands/init_state.rs index 0d065c29442..7af17ca3523 100644 --- a/crates/optimism/cli/src/commands/init_state.rs +++ b/crates/optimism/cli/src/commands/init_state.rs @@ -7,7 +7,7 @@ use reth_cli_commands::common::{AccessRights, CliHeader, CliNodeTypes, Environme use reth_db_common::init::init_from_state_dump; use reth_optimism_chainspec::OpChainSpec; use reth_optimism_primitives::{ - bedrock::{BEDROCK_HEADER, BEDROCK_HEADER_HASH, BEDROCK_HEADER_TTD}, + bedrock::{BEDROCK_HEADER, BEDROCK_HEADER_HASH}, OpPrimitives, }; use reth_primitives_traits::SealedHeader; @@ -58,7 +58,6 @@ impl> InitStateCommandOp { reth_cli_commands::init_state::without_evm::setup_without_evm( &provider_rw, SealedHeader::new(BEDROCK_HEADER, BEDROCK_HEADER_HASH), - BEDROCK_HEADER_TTD, |number| { let mut header = Header::default(); header.set_number(number); diff --git a/crates/optimism/evm/src/build.rs b/crates/optimism/evm/src/build.rs index 087b7f10046..edc877a9a5d 100644 --- a/crates/optimism/evm/src/build.rs +++ b/crates/optimism/evm/src/build.rs @@ -14,6 +14,7 @@ use reth_optimism_consensus::{calculate_receipt_root_no_memo_optimism, isthmus}; use reth_optimism_forks::OpHardforks; use reth_optimism_primitives::DepositReceipt; use reth_primitives_traits::{Receipt, SignedTransaction}; +use revm::context::Block as _; /// Block builder for Optimism. #[derive(Debug)] @@ -53,7 +54,7 @@ impl OpBlockAssembler { } = input; let ctx = ctx.into(); - let timestamp = evm_env.block_env.timestamp.saturating_to(); + let timestamp = evm_env.block_env.timestamp().saturating_to(); let transactions_root = proofs::calculate_transaction_root(&transactions); let receipts_root = @@ -88,19 +89,19 @@ impl OpBlockAssembler { let header = Header { parent_hash: ctx.parent_hash, ommers_hash: EMPTY_OMMER_ROOT_HASH, - beneficiary: evm_env.block_env.beneficiary, + beneficiary: evm_env.block_env.beneficiary(), state_root, transactions_root, receipts_root, withdrawals_root, logs_bloom, timestamp, - mix_hash: evm_env.block_env.prevrandao.unwrap_or_default(), + mix_hash: evm_env.block_env.prevrandao().unwrap_or_default(), nonce: BEACON_NONCE.into(), - base_fee_per_gas: Some(evm_env.block_env.basefee), - number: evm_env.block_env.number.saturating_to(), - gas_limit: evm_env.block_env.gas_limit, - difficulty: evm_env.block_env.difficulty, + base_fee_per_gas: Some(evm_env.block_env.basefee()), + number: evm_env.block_env.number().saturating_to(), + gas_limit: evm_env.block_env.gas_limit(), + difficulty: evm_env.block_env.difficulty(), gas_used: *gas_used, extra_data: ctx.extra_data, parent_beacon_block_root: ctx.parent_beacon_block_root, diff --git a/crates/optimism/evm/src/l1.rs b/crates/optimism/evm/src/l1.rs index a538c8d8690..4165221c987 100644 --- a/crates/optimism/evm/src/l1.rs +++ b/crates/optimism/evm/src/l1.rs @@ -88,10 +88,12 @@ pub fn parse_l1_info_tx_bedrock(data: &[u8]) -> Result Result Result + FromTxWithEncoded - + TransactionEnv, + + TransactionEnv + + OpTxEnv, Precompiles = PrecompilesMap, Spec = OpSpecId, + BlockEnv = BlockEnv, > + Debug, Self: Send + Sync + Unpin + Clone + 'static, { diff --git a/crates/optimism/flashblocks/src/worker.rs b/crates/optimism/flashblocks/src/worker.rs index 68071851f43..8cf7777f6a6 100644 --- a/crates/optimism/flashblocks/src/worker.rs +++ b/crates/optimism/flashblocks/src/worker.rs @@ -124,6 +124,7 @@ where recovered_block: block.into(), execution_output: Arc::new(execution_outcome), hashed_state: Arc::new(hashed_state), + trie_updates: Arc::default(), }, ); let pending_flashblock = PendingFlashBlock::new( diff --git a/crates/optimism/node/tests/it/builder.rs b/crates/optimism/node/tests/it/builder.rs index e0437a5f655..b495fdb47ce 100644 --- a/crates/optimism/node/tests/it/builder.rs +++ b/crates/optimism/node/tests/it/builder.rs @@ -19,7 +19,7 @@ use reth_optimism_node::{args::RollupArgs, OpEvmConfig, OpExecutorBuilder, OpNod use reth_optimism_primitives::OpPrimitives; use reth_provider::providers::BlockchainProvider; use revm::{ - context::{Cfg, ContextTr, TxEnv}, + context::{BlockEnv, Cfg, ContextTr, TxEnv}, context_interface::result::EVMError, inspector::NoOpInspector, interpreter::interpreter::EthInterpreter, @@ -94,6 +94,7 @@ fn test_setup_custom_precompiles() { EVMError; type HaltReason = OpHaltReason; type Spec = OpSpecId; + type BlockEnv = BlockEnv; type Precompiles = PrecompilesMap; fn create_evm( diff --git a/crates/optimism/payload/src/builder.rs b/crates/optimism/payload/src/builder.rs index 1d73464e178..67b8faf5608 100644 --- a/crates/optimism/payload/src/builder.rs +++ b/crates/optimism/payload/src/builder.rs @@ -11,7 +11,7 @@ use alloy_primitives::{B256, U256}; use alloy_rpc_types_debug::ExecutionWitness; use alloy_rpc_types_engine::PayloadId; use reth_basic_payload_builder::*; -use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates}; +use reth_chain_state::ExecutedBlock; use reth_chainspec::{ChainSpecProvider, EthChainSpec}; use reth_evm::{ execute::{ @@ -379,13 +379,11 @@ impl OpBuilder<'_, Txs> { ); // create the executed block data - let executed: ExecutedBlockWithTrieUpdates = ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { - recovered_block: Arc::new(block), - execution_output: Arc::new(execution_outcome), - hashed_state: Arc::new(hashed_state), - }, - trie: ExecutedTrieUpdates::Present(Arc::new(trie_updates)), + let executed: ExecutedBlock = ExecutedBlock { + recovered_block: Arc::new(block), + execution_output: Arc::new(execution_outcome), + hashed_state: Arc::new(hashed_state), + trie_updates: Arc::new(trie_updates), }; let no_tx_pool = ctx.attributes().no_tx_pool(); @@ -567,9 +565,9 @@ where } /// Returns the current fee settings for transactions from the mempool - pub fn best_transaction_attributes(&self, block_env: &BlockEnv) -> BestTransactionsAttributes { + pub fn best_transaction_attributes(&self, block_env: impl Block) -> BestTransactionsAttributes { BestTransactionsAttributes::new( - block_env.basefee, + block_env.basefee(), block_env.blob_gasprice().map(|p| p as u64), ) } @@ -659,10 +657,10 @@ where Transaction: PoolTransaction> + OpPooledTx, >, ) -> Result, PayloadBuilderError> { - let block_gas_limit = builder.evm_mut().block().gas_limit; + let block_gas_limit = builder.evm_mut().block().gas_limit(); let block_da_limit = self.da_config.max_da_block_size(); let tx_da_limit = self.da_config.max_da_tx_size(); - let base_fee = builder.evm_mut().block().basefee; + let base_fee = builder.evm_mut().block().basefee(); while let Some(tx) = best_txs.next(()) { let interop = tx.interop_deadline(); diff --git a/crates/optimism/payload/src/payload.rs b/crates/optimism/payload/src/payload.rs index de1705faa8f..6f530acd853 100644 --- a/crates/optimism/payload/src/payload.rs +++ b/crates/optimism/payload/src/payload.rs @@ -16,7 +16,7 @@ use op_alloy_consensus::{encode_holocene_extra_data, encode_jovian_extra_data, E use op_alloy_rpc_types_engine::{ OpExecutionPayloadEnvelopeV3, OpExecutionPayloadEnvelopeV4, OpExecutionPayloadV4, }; -use reth_chain_state::ExecutedBlockWithTrieUpdates; +use reth_chain_state::ExecutedBlock; use reth_chainspec::EthChainSpec; use reth_optimism_evm::OpNextBlockEnvAttributes; use reth_optimism_forks::OpHardforks; @@ -176,7 +176,7 @@ pub struct OpBuiltPayload { /// Sealed block pub(crate) block: Arc>, /// Block execution data for the payload, if any. - pub(crate) executed_block: Option>, + pub(crate) executed_block: Option>, /// The fees of the block pub(crate) fees: U256, } @@ -189,7 +189,7 @@ impl OpBuiltPayload { id: PayloadId, block: Arc>, fees: U256, - executed_block: Option>, + executed_block: Option>, ) -> Self { Self { id, block, fees, executed_block } } @@ -226,7 +226,7 @@ impl BuiltPayload for OpBuiltPayload { self.fees } - fn executed_block(&self) -> Option> { + fn executed_block(&self) -> Option> { self.executed_block.clone() } diff --git a/crates/optimism/rpc/src/eth/call.rs b/crates/optimism/rpc/src/eth/call.rs index b7ce75c51b2..4e853984ac9 100644 --- a/crates/optimism/rpc/src/eth/call.rs +++ b/crates/optimism/rpc/src/eth/call.rs @@ -1,5 +1,4 @@ use crate::{eth::RpcNodeCore, OpEthApi, OpEthApiError}; -use reth_evm::{SpecFor, TxEnvFor}; use reth_rpc_eth_api::{ helpers::{estimate::EstimateCall, Call, EthCall}, FromEvmError, RpcConvert, @@ -9,12 +8,7 @@ impl EthCall for OpEthApi where N: RpcNodeCore, OpEthApiError: FromEvmError, - Rpc: RpcConvert< - Primitives = N::Primitives, - Error = OpEthApiError, - TxEnv = TxEnvFor, - Spec = SpecFor, - >, + Rpc: RpcConvert, { } @@ -22,12 +16,7 @@ impl EstimateCall for OpEthApi where N: RpcNodeCore, OpEthApiError: FromEvmError, - Rpc: RpcConvert< - Primitives = N::Primitives, - Error = OpEthApiError, - TxEnv = TxEnvFor, - Spec = SpecFor, - >, + Rpc: RpcConvert, { } @@ -35,12 +24,7 @@ impl Call for OpEthApi where N: RpcNodeCore, OpEthApiError: FromEvmError, - Rpc: RpcConvert< - Primitives = N::Primitives, - Error = OpEthApiError, - TxEnv = TxEnvFor, - Spec = SpecFor, - >, + Rpc: RpcConvert, { #[inline] fn call_gas_limit(&self) -> u64 { diff --git a/crates/optimism/rpc/src/eth/mod.rs b/crates/optimism/rpc/src/eth/mod.rs index e10c5152473..04887d98f4c 100644 --- a/crates/optimism/rpc/src/eth/mod.rs +++ b/crates/optimism/rpc/src/eth/mod.rs @@ -26,19 +26,19 @@ use reth_optimism_flashblocks::{ ExecutionPayloadBaseV1, FlashBlockBuildInfo, FlashBlockCompleteSequenceRx, FlashBlockService, InProgressFlashBlockRx, PendingBlockRx, PendingFlashBlock, WsFlashBlockStream, }; -use reth_rpc::eth::{core::EthApiInner, DevSigner}; +use reth_rpc::eth::core::EthApiInner; use reth_rpc_eth_api::{ helpers::{ - pending_block::BuildPendingEnv, AddDevSigners, EthApiSpec, EthFees, EthState, LoadFee, - LoadPendingBlock, LoadState, SpawnBlocking, Trace, + pending_block::BuildPendingEnv, EthApiSpec, EthFees, EthState, LoadFee, LoadPendingBlock, + LoadState, SpawnBlocking, Trace, }, EthApiTypes, FromEvmError, FullEthApiServer, RpcConvert, RpcConverter, RpcNodeCore, - RpcNodeCoreExt, RpcTypes, SignableTxRequest, + RpcNodeCoreExt, RpcTypes, }; use reth_rpc_eth_types::{ EthStateCache, FeeHistoryCache, GasPriceOracle, PendingBlock, PendingBlockEnvOrigin, }; -use reth_storage_api::{ProviderHeader, ProviderTx}; +use reth_storage_api::ProviderHeader; use reth_tasks::{ pool::{BlockingTaskGuard, BlockingTaskPool}, TaskSpawner, @@ -335,18 +335,6 @@ where { } -impl AddDevSigners for OpEthApi -where - N: RpcNodeCore, - Rpc: RpcConvert< - Network: RpcTypes>>, - >, -{ - fn with_dev_accounts(&self) { - *self.inner.eth_api.signers().write() = DevSigner::random_signers(20) - } -} - impl fmt::Debug for OpEthApi { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("OpEthApi").finish_non_exhaustive() @@ -483,7 +471,7 @@ where NetworkT: RpcTypes, OpRpcConvert: RpcConvert, OpEthApi>: - FullEthApiServer + AddDevSigners, + FullEthApiServer, { type EthApi = OpEthApi>; diff --git a/crates/optimism/rpc/src/eth/receipt.rs b/crates/optimism/rpc/src/eth/receipt.rs index 97fe3a0b5b7..f8910c22a33 100644 --- a/crates/optimism/rpc/src/eth/receipt.rs +++ b/crates/optimism/rpc/src/eth/receipt.rs @@ -249,6 +249,7 @@ impl OpReceiptFieldsBuilder { l1_blob_base_fee_scalar, operator_fee_scalar, operator_fee_constant, + da_footprint_gas_scalar: None, }, deposit_nonce, deposit_receipt_version, @@ -364,6 +365,7 @@ mod test { l1_blob_base_fee_scalar: Some(1014213), operator_fee_scalar: None, operator_fee_constant: None, + da_footprint_gas_scalar: None, }, deposit_nonce: None, deposit_receipt_version: None, @@ -407,6 +409,7 @@ mod test { l1_blob_base_fee_scalar, operator_fee_scalar, operator_fee_constant, + .. } = receipt_meta.l1_block_info; assert_eq!( @@ -458,10 +461,11 @@ mod test { OpTransactionSigned::decode_2718(&mut TX_1_OP_MAINNET_BLOCK_124665056.as_slice()) .unwrap(); - let mut l1_block_info = op_revm::L1BlockInfo::default(); - - l1_block_info.operator_fee_scalar = Some(U256::ZERO); - l1_block_info.operator_fee_constant = Some(U256::from(2)); + let mut l1_block_info = op_revm::L1BlockInfo { + operator_fee_scalar: Some(U256::ZERO), + operator_fee_constant: Some(U256::from(2)), + ..Default::default() + }; let receipt_meta = OpReceiptFieldsBuilder::new(BLOCK_124665056_TIMESTAMP, 124665056) .l1_block_info(&*OP_MAINNET, &tx_1, &mut l1_block_info) @@ -481,10 +485,11 @@ mod test { OpTransactionSigned::decode_2718(&mut TX_1_OP_MAINNET_BLOCK_124665056.as_slice()) .unwrap(); - let mut l1_block_info = op_revm::L1BlockInfo::default(); - - l1_block_info.operator_fee_scalar = Some(U256::ZERO); - l1_block_info.operator_fee_constant = Some(U256::ZERO); + let mut l1_block_info = op_revm::L1BlockInfo { + operator_fee_scalar: Some(U256::ZERO), + operator_fee_constant: Some(U256::ZERO), + ..Default::default() + }; let receipt_meta = OpReceiptFieldsBuilder::new(BLOCK_124665056_TIMESTAMP, 124665056) .l1_block_info(&*OP_MAINNET, &tx_1, &mut l1_block_info) @@ -535,6 +540,7 @@ mod test { l1_blob_base_fee_scalar, operator_fee_scalar, operator_fee_constant, + .. } = receipt_meta.l1_block_info; assert_eq!(l1_gas_price, Some(14121491676), "incorrect l1 base fee (former gas price)"); diff --git a/crates/payload/primitives/src/traits.rs b/crates/payload/primitives/src/traits.rs index 39bd14cc63b..160956afa27 100644 --- a/crates/payload/primitives/src/traits.rs +++ b/crates/payload/primitives/src/traits.rs @@ -9,7 +9,7 @@ use alloy_eips::{ use alloy_primitives::{Address, B256, U256}; use alloy_rpc_types_engine::{PayloadAttributes as EthPayloadAttributes, PayloadId}; use core::fmt; -use reth_chain_state::ExecutedBlockWithTrieUpdates; +use reth_chain_state::ExecutedBlock; use reth_primitives_traits::{NodePrimitives, SealedBlock, SealedHeader}; /// Represents a successfully built execution payload (block). @@ -30,7 +30,7 @@ pub trait BuiltPayload: Send + Sync + fmt::Debug { /// Returns the complete execution result including state updates. /// /// Returns `None` if execution data is not available or not tracked. - fn executed_block(&self) -> Option> { + fn executed_block(&self) -> Option> { None } diff --git a/crates/primitives-traits/src/extended.rs b/crates/primitives-traits/src/extended.rs index 4cba4b7d52d..da2bbc533aa 100644 --- a/crates/primitives-traits/src/extended.rs +++ b/crates/primitives-traits/src/extended.rs @@ -142,8 +142,8 @@ where impl SignerRecoverable for Extended where - B: SignedTransaction + IsTyped2718, - T: SignedTransaction, + B: SignerRecoverable, + T: SignerRecoverable, { fn recover_signer(&self) -> Result { delegate!(self => tx.recover_signer()) diff --git a/crates/prune/prune/src/builder.rs b/crates/prune/prune/src/builder.rs index 1987c500da7..f21319bb458 100644 --- a/crates/prune/prune/src/builder.rs +++ b/crates/prune/prune/src/builder.rs @@ -6,8 +6,8 @@ use reth_db_api::{table::Value, transaction::DbTxMut}; use reth_exex_types::FinishedExExHeight; use reth_primitives_traits::NodePrimitives; use reth_provider::{ - providers::StaticFileProvider, BlockReader, DBProvider, DatabaseProviderFactory, - NodePrimitivesProvider, PruneCheckpointReader, PruneCheckpointWriter, + providers::StaticFileProvider, BlockReader, ChainStateBlockReader, DBProvider, + DatabaseProviderFactory, NodePrimitivesProvider, PruneCheckpointReader, PruneCheckpointWriter, StaticFileProviderFactory, }; use reth_prune_types::PruneModes; @@ -83,6 +83,7 @@ impl PrunerBuilder { ProviderRW: PruneCheckpointWriter + PruneCheckpointReader + BlockReader + + ChainStateBlockReader + StaticFileProviderFactory< Primitives: NodePrimitives, >, @@ -113,6 +114,7 @@ impl PrunerBuilder { Primitives: NodePrimitives, > + DBProvider + BlockReader + + ChainStateBlockReader + PruneCheckpointWriter + PruneCheckpointReader, { @@ -132,7 +134,7 @@ impl Default for PrunerBuilder { fn default() -> Self { Self { block_interval: 5, - segments: PruneModes::none(), + segments: PruneModes::default(), delete_limit: MAINNET_PRUNE_DELETE_LIMIT, timeout: None, finished_exex_height: watch::channel(FinishedExExHeight::NoExExs).1, diff --git a/crates/prune/prune/src/segments/mod.rs b/crates/prune/prune/src/segments/mod.rs index 1daade01358..dc175254453 100644 --- a/crates/prune/prune/src/segments/mod.rs +++ b/crates/prune/prune/src/segments/mod.rs @@ -15,8 +15,8 @@ pub use static_file::{ use std::{fmt::Debug, ops::RangeInclusive}; use tracing::error; pub use user::{ - AccountHistory, Receipts as UserReceipts, ReceiptsByLogs, SenderRecovery, StorageHistory, - TransactionLookup, + AccountHistory, MerkleChangeSets, Receipts as UserReceipts, ReceiptsByLogs, SenderRecovery, + StorageHistory, TransactionLookup, }; /// A segment represents a pruning of some portion of the data. diff --git a/crates/prune/prune/src/segments/set.rs b/crates/prune/prune/src/segments/set.rs index 08e41bcdf75..72847219b09 100644 --- a/crates/prune/prune/src/segments/set.rs +++ b/crates/prune/prune/src/segments/set.rs @@ -1,13 +1,13 @@ use crate::segments::{ - AccountHistory, ReceiptsByLogs, Segment, SenderRecovery, StorageHistory, TransactionLookup, - UserReceipts, + AccountHistory, MerkleChangeSets, ReceiptsByLogs, Segment, SenderRecovery, StorageHistory, + TransactionLookup, UserReceipts, }; use alloy_eips::eip2718::Encodable2718; use reth_db_api::{table::Value, transaction::DbTxMut}; use reth_primitives_traits::NodePrimitives; use reth_provider::{ - providers::StaticFileProvider, BlockReader, DBProvider, PruneCheckpointReader, - PruneCheckpointWriter, StaticFileProviderFactory, + providers::StaticFileProvider, BlockReader, ChainStateBlockReader, DBProvider, + PruneCheckpointReader, PruneCheckpointWriter, StaticFileProviderFactory, }; use reth_prune_types::PruneModes; @@ -52,7 +52,8 @@ where > + DBProvider + PruneCheckpointWriter + PruneCheckpointReader - + BlockReader, + + BlockReader + + ChainStateBlockReader, { /// Creates a [`SegmentSet`] from an existing components, such as [`StaticFileProvider`] and /// [`PruneModes`]. @@ -67,6 +68,7 @@ where account_history, storage_history, bodies_history: _, + merkle_changesets, receipts_log_filter, } = prune_modes; @@ -77,6 +79,8 @@ where .segment(StaticFileTransactions::new(static_file_provider.clone())) // Static file receipts .segment(StaticFileReceipts::new(static_file_provider)) + // Merkle changesets + .segment(MerkleChangeSets::new(merkle_changesets)) // Account history .segment_opt(account_history.map(AccountHistory::new)) // Storage history diff --git a/crates/prune/prune/src/segments/user/account_history.rs b/crates/prune/prune/src/segments/user/account_history.rs index 3c18cd1befc..317337f050e 100644 --- a/crates/prune/prune/src/segments/user/account_history.rs +++ b/crates/prune/prune/src/segments/user/account_history.rs @@ -45,7 +45,7 @@ where PrunePurpose::User } - #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] + #[instrument(target = "pruner", skip(self, provider), ret(level = "trace"))] fn prune(&self, provider: &Provider, input: PruneInput) -> Result { let range = match input.get_next_block_range() { Some(range) => range, diff --git a/crates/prune/prune/src/segments/user/merkle_change_sets.rs b/crates/prune/prune/src/segments/user/merkle_change_sets.rs new file mode 100644 index 00000000000..89cc4567b7d --- /dev/null +++ b/crates/prune/prune/src/segments/user/merkle_change_sets.rs @@ -0,0 +1,116 @@ +use crate::{ + db_ext::DbTxPruneExt, + segments::{PruneInput, Segment}, + PrunerError, +}; +use alloy_primitives::B256; +use reth_db_api::{models::BlockNumberHashedAddress, table::Value, tables, transaction::DbTxMut}; +use reth_primitives_traits::NodePrimitives; +use reth_provider::{ + errors::provider::ProviderResult, BlockReader, ChainStateBlockReader, DBProvider, + NodePrimitivesProvider, PruneCheckpointWriter, TransactionsProvider, +}; +use reth_prune_types::{ + PruneCheckpoint, PruneMode, PrunePurpose, PruneSegment, SegmentOutput, SegmentOutputCheckpoint, +}; +use tracing::{instrument, trace}; + +#[derive(Debug)] +pub struct MerkleChangeSets { + mode: PruneMode, +} + +impl MerkleChangeSets { + pub const fn new(mode: PruneMode) -> Self { + Self { mode } + } +} + +impl Segment for MerkleChangeSets +where + Provider: DBProvider + + PruneCheckpointWriter + + TransactionsProvider + + BlockReader + + ChainStateBlockReader + + NodePrimitivesProvider>, +{ + fn segment(&self) -> PruneSegment { + PruneSegment::MerkleChangeSets + } + + fn mode(&self) -> Option { + Some(self.mode) + } + + fn purpose(&self) -> PrunePurpose { + PrunePurpose::User + } + + #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] + fn prune(&self, provider: &Provider, input: PruneInput) -> Result { + let Some(block_range) = input.get_next_block_range() else { + trace!(target: "pruner", "No change sets to prune"); + return Ok(SegmentOutput::done()) + }; + + let block_range_end = *block_range.end(); + let mut limiter = input.limiter; + + // Create range for StoragesTrieChangeSets which uses BlockNumberHashedAddress as key + let storage_range_start: BlockNumberHashedAddress = + (*block_range.start(), B256::ZERO).into(); + let storage_range_end: BlockNumberHashedAddress = + (*block_range.end() + 1, B256::ZERO).into(); + let storage_range = storage_range_start..storage_range_end; + + let mut last_storages_pruned_block = None; + let (storages_pruned, done) = + provider.tx_ref().prune_table_with_range::( + storage_range, + &mut limiter, + |_| false, + |(BlockNumberHashedAddress((block_number, _)), _)| { + last_storages_pruned_block = Some(block_number); + }, + )?; + + trace!(target: "pruner", %storages_pruned, %done, "Pruned storages change sets"); + + let mut last_accounts_pruned_block = block_range_end; + let last_storages_pruned_block = last_storages_pruned_block + // If there's more storage changesets to prune, set the checkpoint block number to + // previous, so we could finish pruning its storage changesets on the next run. + .map(|block_number| if done { block_number } else { block_number.saturating_sub(1) }) + .unwrap_or(block_range_end); + + let (accounts_pruned, done) = + provider.tx_ref().prune_table_with_range::( + block_range, + &mut limiter, + |_| false, + |row| last_accounts_pruned_block = row.0, + )?; + + trace!(target: "pruner", %accounts_pruned, %done, "Pruned accounts change sets"); + + let progress = limiter.progress(done); + + Ok(SegmentOutput { + progress, + pruned: accounts_pruned + storages_pruned, + checkpoint: Some(SegmentOutputCheckpoint { + block_number: Some(last_storages_pruned_block.min(last_accounts_pruned_block)), + tx_number: None, + }), + }) + } + + fn save_checkpoint( + &self, + provider: &Provider, + checkpoint: PruneCheckpoint, + ) -> ProviderResult<()> { + provider.save_prune_checkpoint(PruneSegment::MerkleChangeSets, checkpoint) + } +} diff --git a/crates/prune/prune/src/segments/user/mod.rs b/crates/prune/prune/src/segments/user/mod.rs index 0b787d14dae..c25bc6bc764 100644 --- a/crates/prune/prune/src/segments/user/mod.rs +++ b/crates/prune/prune/src/segments/user/mod.rs @@ -1,5 +1,6 @@ mod account_history; mod history; +mod merkle_change_sets; mod receipts; mod receipts_by_logs; mod sender_recovery; @@ -7,6 +8,7 @@ mod storage_history; mod transaction_lookup; pub use account_history::AccountHistory; +pub use merkle_change_sets::MerkleChangeSets; pub use receipts::Receipts; pub use receipts_by_logs::ReceiptsByLogs; pub use sender_recovery::SenderRecovery; diff --git a/crates/prune/prune/src/segments/user/receipts.rs b/crates/prune/prune/src/segments/user/receipts.rs index ecb0f3423be..03faddc1d5b 100644 --- a/crates/prune/prune/src/segments/user/receipts.rs +++ b/crates/prune/prune/src/segments/user/receipts.rs @@ -42,7 +42,7 @@ where PrunePurpose::User } - #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] + #[instrument(target = "pruner", skip(self, provider), ret(level = "trace"))] fn prune(&self, provider: &Provider, input: PruneInput) -> Result { crate::segments::receipts::prune(provider, input) } diff --git a/crates/prune/prune/src/segments/user/receipts_by_logs.rs b/crates/prune/prune/src/segments/user/receipts_by_logs.rs index 0849db52518..8fd6d1e73a5 100644 --- a/crates/prune/prune/src/segments/user/receipts_by_logs.rs +++ b/crates/prune/prune/src/segments/user/receipts_by_logs.rs @@ -45,7 +45,7 @@ where PrunePurpose::User } - #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] + #[instrument(target = "pruner", skip(self, provider), ret(level = "trace"))] fn prune(&self, provider: &Provider, input: PruneInput) -> Result { // Contract log filtering removes every receipt possible except the ones in the list. So, // for the other receipts it's as if they had a `PruneMode::Distance()` of diff --git a/crates/prune/prune/src/segments/user/sender_recovery.rs b/crates/prune/prune/src/segments/user/sender_recovery.rs index 35ee487203a..9fbad8c428c 100644 --- a/crates/prune/prune/src/segments/user/sender_recovery.rs +++ b/crates/prune/prune/src/segments/user/sender_recovery.rs @@ -37,7 +37,7 @@ where PrunePurpose::User } - #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] + #[instrument(target = "pruner", skip(self, provider), ret(level = "trace"))] fn prune(&self, provider: &Provider, input: PruneInput) -> Result { let tx_range = match input.get_next_tx_num_range(provider)? { Some(range) => range, diff --git a/crates/prune/prune/src/segments/user/storage_history.rs b/crates/prune/prune/src/segments/user/storage_history.rs index ee7447c37da..a4ad37bf789 100644 --- a/crates/prune/prune/src/segments/user/storage_history.rs +++ b/crates/prune/prune/src/segments/user/storage_history.rs @@ -47,7 +47,7 @@ where PrunePurpose::User } - #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] + #[instrument(target = "pruner", skip(self, provider), ret(level = "trace"))] fn prune(&self, provider: &Provider, input: PruneInput) -> Result { let range = match input.get_next_block_range() { Some(range) => range, diff --git a/crates/prune/prune/src/segments/user/transaction_lookup.rs b/crates/prune/prune/src/segments/user/transaction_lookup.rs index e218f623ed5..0055f8abd22 100644 --- a/crates/prune/prune/src/segments/user/transaction_lookup.rs +++ b/crates/prune/prune/src/segments/user/transaction_lookup.rs @@ -38,7 +38,7 @@ where PrunePurpose::User } - #[instrument(level = "trace", target = "pruner", skip(self, provider), ret)] + #[instrument(target = "pruner", skip(self, provider), ret(level = "trace"))] fn prune( &self, provider: &Provider, diff --git a/crates/prune/types/src/segment.rs b/crates/prune/types/src/segment.rs index e131f353fe3..0d60d900137 100644 --- a/crates/prune/types/src/segment.rs +++ b/crates/prune/types/src/segment.rs @@ -3,6 +3,9 @@ use derive_more::Display; use thiserror::Error; /// Segment of the data that can be pruned. +/// +/// NOTE new variants must be added to the end of this enum. The variant index is encoded directly +/// when writing to the `PruneCheckpoint` table, so changing the order here will corrupt the table. #[derive(Debug, Display, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] #[cfg_attr(test, derive(arbitrary::Arbitrary))] #[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::Compact))] @@ -26,6 +29,9 @@ pub enum PruneSegment { Headers, /// Prune segment responsible for the `Transactions` table. Transactions, + /// Prune segment responsible for all rows in `AccountsTrieChangeSets` and + /// `StoragesTrieChangeSets` table. + MerkleChangeSets, } #[cfg(test)] @@ -44,9 +50,10 @@ impl PruneSegment { 0 } Self::Receipts if purpose.is_static_file() => 0, - Self::ContractLogs | Self::AccountHistory | Self::StorageHistory => { - MINIMUM_PRUNING_DISTANCE - } + Self::ContractLogs | + Self::AccountHistory | + Self::StorageHistory | + Self::MerkleChangeSets | Self::Receipts => MINIMUM_PRUNING_DISTANCE, } } diff --git a/crates/prune/types/src/target.rs b/crates/prune/types/src/target.rs index 574a0e2e555..657cf6a37c5 100644 --- a/crates/prune/types/src/target.rs +++ b/crates/prune/types/src/target.rs @@ -36,8 +36,13 @@ pub enum HistoryType { StorageHistory, } +/// Default pruning mode for merkle changesets +const fn default_merkle_changesets_mode() -> PruneMode { + PruneMode::Distance(MINIMUM_PRUNING_DISTANCE) +} + /// Pruning configuration for every segment of the data that can be pruned. -#[derive(Debug, Clone, Default, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] #[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(any(test, feature = "serde"), serde(default))] pub struct PruneModes { @@ -84,6 +89,16 @@ pub struct PruneModes { ) )] pub bodies_history: Option, + /// Merkle Changesets pruning configuration for `AccountsTrieChangeSets` and + /// `StoragesTrieChangeSets`. + #[cfg_attr( + any(test, feature = "serde"), + serde( + default = "default_merkle_changesets_mode", + deserialize_with = "deserialize_prune_mode_with_min_blocks::" + ) + )] + pub merkle_changesets: PruneMode, /// Receipts pruning configuration by retaining only those receipts that contain logs emitted /// by the specified addresses, discarding others. This setting is overridden by `receipts`. /// @@ -92,12 +107,22 @@ pub struct PruneModes { pub receipts_log_filter: ReceiptsLogPruneConfig, } -impl PruneModes { - /// Sets pruning to no target. - pub fn none() -> Self { - Self::default() +impl Default for PruneModes { + fn default() -> Self { + Self { + sender_recovery: None, + transaction_lookup: None, + receipts: None, + account_history: None, + storage_history: None, + bodies_history: None, + merkle_changesets: default_merkle_changesets_mode(), + receipts_log_filter: ReceiptsLogPruneConfig::default(), + } } +} +impl PruneModes { /// Sets pruning to all targets. pub fn all() -> Self { Self { @@ -107,6 +132,7 @@ impl PruneModes { account_history: Some(PruneMode::Full), storage_history: Some(PruneMode::Full), bodies_history: Some(PruneMode::Full), + merkle_changesets: PruneMode::Full, receipts_log_filter: Default::default(), } } @@ -116,11 +142,6 @@ impl PruneModes { self.receipts.is_some() || !self.receipts_log_filter.is_empty() } - /// Returns true if all prune modes are set to [`None`]. - pub fn is_empty(&self) -> bool { - self == &Self::none() - } - /// Returns an error if we can't unwind to the targeted block because the target block is /// outside the range. /// @@ -170,6 +191,28 @@ impl PruneModes { } } +/// Deserializes [`PruneMode`] and validates that the value is not less than the const +/// generic parameter `MIN_BLOCKS`. This parameter represents the number of blocks that needs to be +/// left in database after the pruning. +/// +/// 1. For [`PruneMode::Full`], it fails if `MIN_BLOCKS > 0`. +/// 2. For [`PruneMode::Distance`], it fails if `distance < MIN_BLOCKS + 1`. `+ 1` is needed because +/// `PruneMode::Distance(0)` means that we leave zero blocks from the latest, meaning we have one +/// block in the database. +#[cfg(any(test, feature = "serde"))] +fn deserialize_prune_mode_with_min_blocks< + 'de, + const MIN_BLOCKS: u64, + D: serde::Deserializer<'de>, +>( + deserializer: D, +) -> Result { + use serde::Deserialize; + let prune_mode = PruneMode::deserialize(deserializer)?; + serde_deserialize_validate::(&prune_mode)?; + Ok(prune_mode) +} + /// Deserializes [`Option`] and validates that the value is not less than the const /// generic parameter `MIN_BLOCKS`. This parameter represents the number of blocks that needs to be /// left in database after the pruning. @@ -186,12 +229,21 @@ fn deserialize_opt_prune_mode_with_min_blocks< >( deserializer: D, ) -> Result, D::Error> { - use alloc::format; use serde::Deserialize; let prune_mode = Option::::deserialize(deserializer)?; + if let Some(prune_mode) = prune_mode.as_ref() { + serde_deserialize_validate::(prune_mode)?; + } + Ok(prune_mode) +} +#[cfg(any(test, feature = "serde"))] +fn serde_deserialize_validate<'a, 'de, const MIN_BLOCKS: u64, D: serde::Deserializer<'de>>( + prune_mode: &'a PruneMode, +) -> Result<(), D::Error> { + use alloc::format; match prune_mode { - Some(PruneMode::Full) if MIN_BLOCKS > 0 => { + PruneMode::Full if MIN_BLOCKS > 0 => { Err(serde::de::Error::invalid_value( serde::de::Unexpected::Str("full"), // This message should have "expected" wording @@ -199,15 +251,15 @@ fn deserialize_opt_prune_mode_with_min_blocks< .as_str(), )) } - Some(PruneMode::Distance(distance)) if distance < MIN_BLOCKS => { + PruneMode::Distance(distance) if *distance < MIN_BLOCKS => { Err(serde::de::Error::invalid_value( - serde::de::Unexpected::Unsigned(distance), + serde::de::Unexpected::Unsigned(*distance), // This message should have "expected" wording &format!("prune mode that leaves at least {MIN_BLOCKS} blocks in the database") .as_str(), )) } - _ => Ok(prune_mode), + _ => Ok(()), } } @@ -240,7 +292,7 @@ mod tests { #[test] fn test_unwind_target_unpruned() { // Test case 1: No pruning configured - should always succeed - let prune_modes = PruneModes::none(); + let prune_modes = PruneModes::default(); assert!(prune_modes.ensure_unwind_target_unpruned(1000, 500, &[]).is_ok()); assert!(prune_modes.ensure_unwind_target_unpruned(1000, 0, &[]).is_ok()); diff --git a/crates/ress/provider/src/lib.rs b/crates/ress/provider/src/lib.rs index 599b37962f0..d986eb9e953 100644 --- a/crates/ress/provider/src/lib.rs +++ b/crates/ress/provider/src/lib.rs @@ -11,9 +11,7 @@ use alloy_consensus::BlockHeader as _; use alloy_primitives::{Bytes, B256}; use parking_lot::Mutex; -use reth_chain_state::{ - ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates, MemoryOverlayStateProvider, -}; +use reth_chain_state::{ExecutedBlock, MemoryOverlayStateProvider}; use reth_errors::{ProviderError, ProviderResult}; use reth_ethereum_primitives::{Block, BlockBody, EthPrimitives}; use reth_evm::{execute::Executor, ConfigureEvm}; @@ -125,10 +123,8 @@ where self.pending_state.invalid_recovered_block(&ancestor_hash) { trace!(target: "reth::ress_provider", %block_hash, %ancestor_hash, "Using invalid ancestor block for witness construction"); - executed = Some(ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { recovered_block: invalid, ..Default::default() }, - trie: ExecutedTrieUpdates::empty(), - }); + executed = + Some(ExecutedBlock { recovered_block: invalid, ..Default::default() }); } let Some(executed) = executed else { @@ -162,14 +158,8 @@ where let witness_state_provider = self.provider.state_by_block_hash(ancestor_hash)?; let mut trie_input = TrieInput::default(); for block in executed_ancestors.into_iter().rev() { - if let Some(trie_updates) = block.trie.as_ref() { - trie_input.append_cached_ref(trie_updates, &block.hashed_state); - } else { - trace!(target: "reth::ress_provider", ancestor = ?block.recovered_block().num_hash(), "Missing trie updates for ancestor block"); - return Err(ProviderError::TrieWitnessError( - "missing trie updates for ancestor".to_owned(), - )); - } + let trie_updates = block.trie_updates.as_ref(); + trie_input.append_cached_ref(trie_updates, &block.hashed_state); } let mut hashed_state = db.into_state(); hashed_state.extend(record.hashed_state); diff --git a/crates/ress/provider/src/pending_state.rs b/crates/ress/provider/src/pending_state.rs index e1a84661fc2..f536acdb60a 100644 --- a/crates/ress/provider/src/pending_state.rs +++ b/crates/ress/provider/src/pending_state.rs @@ -5,7 +5,7 @@ use alloy_primitives::{ }; use futures::StreamExt; use parking_lot::RwLock; -use reth_chain_state::ExecutedBlockWithTrieUpdates; +use reth_chain_state::ExecutedBlock; use reth_ethereum_primitives::EthPrimitives; use reth_node_api::{ConsensusEngineEvent, NodePrimitives}; use reth_primitives_traits::{Bytecode, RecoveredBlock}; @@ -20,14 +20,14 @@ pub struct PendingState(Arc>>); #[derive(Default, Debug)] struct PendingStateInner { - blocks_by_hash: B256Map>, + blocks_by_hash: B256Map>, invalid_blocks_by_hash: B256Map>>, block_hashes_by_number: BTreeMap, } impl PendingState { /// Insert executed block with trie updates. - pub fn insert_block(&self, block: ExecutedBlockWithTrieUpdates) { + pub fn insert_block(&self, block: ExecutedBlock) { let mut this = self.0.write(); let block_hash = block.recovered_block.hash(); this.block_hashes_by_number @@ -46,13 +46,13 @@ impl PendingState { } /// Returns only valid executed blocks by hash. - pub fn executed_block(&self, hash: &B256) -> Option> { + pub fn executed_block(&self, hash: &B256) -> Option> { self.0.read().blocks_by_hash.get(hash).cloned() } /// Returns valid recovered block. pub fn recovered_block(&self, hash: &B256) -> Option>> { - self.executed_block(hash).map(|b| b.recovered_block.clone()) + self.executed_block(hash).map(|b| b.recovered_block) } /// Returns invalid recovered block. diff --git a/crates/rpc/ipc/src/server/ipc.rs b/crates/rpc/ipc/src/server/ipc.rs index 19992ead498..fda19c7cb31 100644 --- a/crates/rpc/ipc/src/server/ipc.rs +++ b/crates/rpc/ipc/src/server/ipc.rs @@ -27,7 +27,7 @@ pub(crate) struct Batch { // Batch responses must be sent back as a single message so we read the results from each // request in the batch and read the results off of a new channel, `rx_batch`, and then send the // complete batch response back to the client over `tx`. -#[instrument(name = "batch", skip(b), level = "TRACE")] +#[instrument(name = "batch", skip(b))] pub(crate) async fn process_batch_request( b: Batch, max_response_body_size: usize, @@ -98,7 +98,7 @@ where } } -#[instrument(name = "method_call", fields(method = req.method.as_ref()), skip(req, rpc_service), level = "TRACE")] +#[instrument(name = "method_call", fields(method = req.method.as_ref()), skip(req, rpc_service))] pub(crate) async fn execute_call_with_tracing<'a, S>( req: Request<'a>, rpc_service: &S, diff --git a/crates/rpc/ipc/src/server/mod.rs b/crates/rpc/ipc/src/server/mod.rs index b6114938d2b..6e6b092c408 100644 --- a/crates/rpc/ipc/src/server/mod.rs +++ b/crates/rpc/ipc/src/server/mod.rs @@ -443,7 +443,7 @@ struct ProcessConnection<'a, HttpMiddleware, RpcMiddleware> { } /// Spawns the IPC connection onto a new task -#[instrument(name = "connection", skip_all, fields(conn_id = %params.conn_id), level = "INFO")] +#[instrument(name = "connection", skip_all, fields(conn_id = %params.conn_id))] fn process_connection( params: ProcessConnection<'_, HttpMiddleware, RpcMiddleware>, ) where diff --git a/crates/rpc/rpc-convert/src/transaction.rs b/crates/rpc/rpc-convert/src/transaction.rs index b8fb25c66c4..046acbda544 100644 --- a/crates/rpc/rpc-convert/src/transaction.rs +++ b/crates/rpc/rpc-convert/src/transaction.rs @@ -2,7 +2,7 @@ use crate::{ fees::{CallFees, CallFeesError}, - RpcHeader, RpcReceipt, RpcTransaction, RpcTxReq, RpcTypes, + RpcHeader, RpcReceipt, RpcTransaction, RpcTxReq, RpcTypes, SignableTxRequest, }; use alloy_consensus::{ error::ValueError, transaction::Recovered, EthereumTxEnvelope, Sealable, TxEip4844, @@ -17,7 +17,7 @@ use core::error; use dyn_clone::DynClone; use reth_evm::{ revm::context_interface::{either::Either, Block}, - ConfigureEvm, SpecFor, TxEnvFor, + BlockEnvFor, ConfigureEvm, EvmEnvFor, TxEnvFor, }; use reth_primitives_traits::{ BlockTy, HeaderTy, NodePrimitives, SealedBlock, SealedHeader, SealedHeaderFor, TransactionMeta, @@ -123,19 +123,16 @@ pub trait RpcConvert: Send + Sync + Unpin + Debug + DynClone + 'static { /// Associated lower layer consensus types to convert from and into types of [`Self::Network`]. type Primitives: NodePrimitives; + /// The EVM configuration. + type Evm: ConfigureEvm; + /// Associated upper layer JSON-RPC API network requests and responses to convert from and into /// types of [`Self::Primitives`]. - type Network: RpcTypes + Send + Sync + Unpin + Clone + Debug; - - /// A set of variables for executing a transaction. - type TxEnv; + type Network: RpcTypes>>; /// An associated RPC conversion error. type Error: error::Error + Into>; - /// The EVM specification identifier. - type Spec; - /// Wrapper for `fill()` with default `TransactionInfo` /// Create a new rpc transaction result for a _pending_ signed transaction, setting block /// environment related fields to `None`. @@ -169,9 +166,8 @@ pub trait RpcConvert: Send + Sync + Unpin + Debug + DynClone + 'static { fn tx_env( &self, request: RpcTxReq, - cfg_env: &CfgEnv, - block_env: &BlockEnv, - ) -> Result; + evm_env: &EvmEnvFor, + ) -> Result, Self::Error>; /// Converts a set of primitive receipts to RPC representations. It is guaranteed that all /// receipts are from the same block. @@ -199,8 +195,8 @@ pub trait RpcConvert: Send + Sync + Unpin + Debug + DynClone + 'static { } dyn_clone::clone_trait_object!( - - RpcConvert + + RpcConvert ); /// Converts `self` into `T`. The opposite of [`FromConsensusTx`]. @@ -439,7 +435,7 @@ impl TryIntoSimTx> for TransactionRequest { /// implementation for free, thanks to the blanket implementation, unless the conversion requires /// more context. For example, some configuration parameters or access handles to database, network, /// etc. -pub trait TxEnvConverter: +pub trait TxEnvConverter: Debug + Send + Sync + Unpin + Clone + 'static { /// An associated error that can occur during conversion. @@ -451,31 +447,30 @@ pub trait TxEnvConverter: fn convert_tx_env( &self, tx_req: TxReq, - cfg_env: &CfgEnv, - block_env: &BlockEnv, - ) -> Result; + evm_env: &EvmEnvFor, + ) -> Result, Self::Error>; } -impl TxEnvConverter for () +impl TxEnvConverter for () where - TxReq: TryIntoTxEnv, + TxReq: TryIntoTxEnv, BlockEnvFor>, + Evm: ConfigureEvm, { type Error = TxReq::Err; fn convert_tx_env( &self, tx_req: TxReq, - cfg_env: &CfgEnv, - block_env: &BlockEnv, - ) -> Result { - tx_req.try_into_tx_env(cfg_env, block_env) + evm_env: &EvmEnvFor, + ) -> Result, Self::Error> { + tx_req.try_into_tx_env(&evm_env.cfg_env, &evm_env.block_env) } } /// Converts rpc transaction requests into transaction environment using a closure. -impl TxEnvConverter for F +impl TxEnvConverter for F where - F: Fn(TxReq, &CfgEnv, &BlockEnv) -> Result + F: Fn(TxReq, &EvmEnvFor) -> Result, E> + Debug + Send + Sync @@ -483,6 +478,7 @@ where + Clone + 'static, TxReq: Clone, + Evm: ConfigureEvm, E: error::Error + Send + Sync + 'static, { type Error = E; @@ -490,17 +486,16 @@ where fn convert_tx_env( &self, tx_req: TxReq, - cfg_env: &CfgEnv, - block_env: &BlockEnv, - ) -> Result { - self(tx_req, cfg_env, block_env) + evm_env: &EvmEnvFor, + ) -> Result, Self::Error> { + self(tx_req, evm_env) } } /// Converts `self` into `T`. /// /// Should create an executable transaction environment using [`TransactionRequest`]. -pub trait TryIntoTxEnv { +pub trait TryIntoTxEnv { /// An associated error that can occur during the conversion. type Err; @@ -836,7 +831,6 @@ impl } /// Converts `self` into a boxed converter. - #[expect(clippy::type_complexity)] pub fn erased( self, ) -> Box< @@ -844,8 +838,7 @@ impl Primitives = ::Primitives, Network = ::Network, Error = ::Error, - TxEnv = ::TxEnv, - Spec = ::Spec, + Evm = ::Evm, >, > where @@ -908,7 +901,7 @@ impl RpcConvert for RpcConverter where N: NodePrimitives, - Network: RpcTypes + Send + Sync + Unpin + Clone + Debug, + Network: RpcTypes>, Evm: ConfigureEvm + 'static, Receipt: ReceiptConverter< N, @@ -933,13 +926,12 @@ where SimTx: SimTxConverter, TxTy>, RpcTx: RpcTxConverter, Network::TransactionResponse, >>::Out>, - TxEnv: TxEnvConverter, TxEnvFor, SpecFor>, + TxEnv: TxEnvConverter, Evm>, { type Primitives = N; + type Evm = Evm; type Network = Network; - type TxEnv = TxEnvFor; type Error = Receipt::Error; - type Spec = SpecFor; fn fill( &self, @@ -965,10 +957,9 @@ where fn tx_env( &self, request: RpcTxReq, - cfg_env: &CfgEnv>, - block_env: &BlockEnv, - ) -> Result { - self.tx_env_converter.convert_tx_env(request, cfg_env, block_env).map_err(Into::into) + evm_env: &EvmEnvFor, + ) -> Result, Self::Error> { + self.tx_env_converter.convert_tx_env(request, evm_env).map_err(Into::into) } fn convert_receipts( diff --git a/crates/rpc/rpc-eth-api/Cargo.toml b/crates/rpc/rpc-eth-api/Cargo.toml index 44637d1931c..88a7f059323 100644 --- a/crates/rpc/rpc-eth-api/Cargo.toml +++ b/crates/rpc/rpc-eth-api/Cargo.toml @@ -13,7 +13,7 @@ workspace = true [dependencies] # reth -revm = { workspace = true, features = ["optional_block_gas_limit", "optional_eip3607", "optional_no_base_fee"] } +revm = { workspace = true, features = ["optional_block_gas_limit", "optional_eip3607", "optional_no_base_fee", "optional_fee_charge"] } reth-chain-state.workspace = true revm-inspectors.workspace = true reth-primitives-traits = { workspace = true, features = ["rpc-compat"] } diff --git a/crates/rpc/rpc-eth-api/src/helpers/call.rs b/crates/rpc/rpc-eth-api/src/helpers/call.rs index b96dab882a0..221fef3680f 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/call.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/call.rs @@ -20,8 +20,8 @@ use alloy_rpc_types_eth::{ use futures::Future; use reth_errors::{ProviderError, RethError}; use reth_evm::{ - ConfigureEvm, Evm, EvmEnv, EvmEnvFor, HaltReasonFor, InspectorFor, SpecFor, TransactionEnv, - TxEnvFor, + env::BlockEnvironment, ConfigureEvm, Evm, EvmEnvFor, HaltReasonFor, InspectorFor, + TransactionEnv, TxEnvFor, }; use reth_node_api::BlockBody; use reth_primitives_traits::Recovered; @@ -38,6 +38,7 @@ use reth_rpc_eth_types::{ }; use reth_storage_api::{BlockIdReader, ProviderTx}; use revm::{ + context::Block, context_interface::{ result::{ExecutionResult, ResultAndState}, Transaction, @@ -115,7 +116,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA evm_env.cfg_env.disable_nonce_check = true; evm_env.cfg_env.disable_base_fee = true; evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); - evm_env.block_env.basefee = 0; + evm_env.block_env.inner_mut().basefee = 0; } let SimBlock { block_overrides, state_overrides, calls } = block; @@ -123,19 +124,23 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA if let Some(block_overrides) = block_overrides { // ensure we don't allow uncapped gas limit per block if let Some(gas_limit_override) = block_overrides.gas_limit && - gas_limit_override > evm_env.block_env.gas_limit && + gas_limit_override > evm_env.block_env.gas_limit() && gas_limit_override > this.call_gas_limit() { return Err(EthApiError::other(EthSimulateError::GasLimitReached).into()) } - apply_block_overrides(block_overrides, &mut db, &mut evm_env.block_env); + apply_block_overrides( + block_overrides, + &mut db, + evm_env.block_env.inner_mut(), + ); } if let Some(state_overrides) = state_overrides { apply_state_overrides(state_overrides, &mut db) .map_err(Self::Error::from_eth_err)?; } - let block_gas_limit = evm_env.block_env.gas_limit; + let block_gas_limit = evm_env.block_env.gas_limit(); let chain_id = evm_env.cfg_env.chain_id; let default_gas_limit = { @@ -295,7 +300,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA } // transact all bundles - for bundle in bundles { + for (bundle_index, bundle) in bundles.into_iter().enumerate() { let Bundle { transactions, block_override } = bundle; if transactions.is_empty() { // Skip empty bundles @@ -306,15 +311,30 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA let block_overrides = block_override.map(Box::new); // transact all transactions in the bundle - for tx in transactions { + for (tx_index, tx) in transactions.into_iter().enumerate() { // Apply overrides, state overrides are only applied for the first tx in the // request let overrides = EvmOverrides::new(state_override.take(), block_overrides.clone()); - let (current_evm_env, prepared_tx) = - this.prepare_call_env(evm_env.clone(), tx, &mut db, overrides)?; - let res = this.transact(&mut db, current_evm_env, prepared_tx)?; + let (current_evm_env, prepared_tx) = this + .prepare_call_env(evm_env.clone(), tx, &mut db, overrides) + .map_err(|err| { + Self::Error::from_eth_err(EthApiError::call_many_error( + bundle_index, + tx_index, + err.into(), + )) + })?; + let res = this.transact(&mut db, current_evm_env, prepared_tx).map_err( + |err| { + Self::Error::from_eth_err(EthApiError::call_many_error( + bundle_index, + tx_index, + err.into(), + )) + }, + )?; match ensure_success::<_, Self::Error>(res.result) { Ok(output) => { @@ -404,7 +424,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA let cap = this.caller_gas_allowance(&mut db, &evm_env, &tx_env)?; // no gas limit was provided in the request, so we need to cap the request's gas // limit - tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit)); + tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit())); } // can consume the list since we're not using the request anymore @@ -461,7 +481,7 @@ pub trait EthCall: EstimateCall + Call + LoadPendingBlock + LoadBlock + FullEthA /// Executes code on state. pub trait Call: LoadState< - RpcConvert: RpcConvert, Spec = SpecFor>, + RpcConvert: RpcConvert, Error: FromEvmError + From<::Error> + From, @@ -520,7 +540,7 @@ pub trait Call: Ok(res) } - /// Executes the [`EvmEnv`] against the given [Database] without committing state + /// Executes the [`reth_evm::EvmEnv`] against the given [Database] without committing state /// changes. fn transact_with_inspector( &self, @@ -574,7 +594,7 @@ pub trait Call: /// Prepares the state and env for the given [`RpcTxReq`] at the given [`BlockId`] and /// executes the closure on a new task returning the result of the closure. /// - /// This returns the configured [`EvmEnv`] for the given [`RpcTxReq`] at + /// This returns the configured [`reth_evm::EvmEnv`] for the given [`RpcTxReq`] at /// the given [`BlockId`] and with configured call settings: `prepare_call_env`. /// /// This is primarily used by `eth_call`. @@ -712,10 +732,10 @@ pub trait Call: /// /// All `TxEnv` fields are derived from the given [`RpcTxReq`], if fields are - /// `None`, they fall back to the [`EvmEnv`]'s settings. + /// `None`, they fall back to the [`reth_evm::EvmEnv`]'s settings. fn create_txn_env( &self, - evm_env: &EvmEnv>, + evm_env: &EvmEnvFor, mut request: RpcTxReq<::Network>, mut db: impl Database>, ) -> Result, Self::Error> { @@ -728,10 +748,10 @@ pub trait Call: request.as_mut().set_nonce(nonce); } - Ok(self.tx_resp_builder().tx_env(request, &evm_env.cfg_env, &evm_env.block_env)?) + Ok(self.tx_resp_builder().tx_env(request, evm_env)?) } - /// Prepares the [`EvmEnv`] for execution of calls. + /// Prepares the [`reth_evm::EvmEnv`] for execution of calls. /// /// Does not commit any changes to the underlying database. /// @@ -786,11 +806,16 @@ pub trait Call: // Disable EIP-7825 transaction gas limit to support larger transactions evm_env.cfg_env.tx_gas_limit_cap = Some(u64::MAX); + // Disable additional fee charges, e.g. opstack operator fee charge + // See: + // + evm_env.cfg_env.disable_fee_charge = true; + // set nonce to None so that the correct nonce is chosen by the EVM request.as_mut().take_nonce(); if let Some(block_overrides) = overrides.block { - apply_block_overrides(*block_overrides, db, &mut evm_env.block_env); + apply_block_overrides(*block_overrides, db, evm_env.block_env.inner_mut()); } if let Some(state_overrides) = overrides.state { apply_state_overrides(state_overrides, db) @@ -801,7 +826,7 @@ pub trait Call: // lower the basefee to 0 to avoid breaking EVM invariants (basefee < gasprice): if tx_env.gas_price() == 0 { - evm_env.block_env.basefee = 0; + evm_env.block_env.inner_mut().basefee = 0; } if !request_has_gas_limit { @@ -811,7 +836,7 @@ pub trait Call: trace!(target: "rpc::eth::call", ?tx_env, "Applying gas limit cap with caller allowance"); let cap = self.caller_gas_allowance(db, &evm_env, &tx_env)?; // ensure we cap gas_limit to the block's - tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit)); + tx_env.set_gas_limit(cap.min(evm_env.block_env.gas_limit())); } } diff --git a/crates/rpc/rpc-eth-api/src/helpers/estimate.rs b/crates/rpc/rpc-eth-api/src/helpers/estimate.rs index cca674e9739..cd2518345ce 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/estimate.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/estimate.rs @@ -18,7 +18,10 @@ use reth_rpc_eth_types::{ }; use reth_rpc_server_types::constants::gas_oracle::{CALL_STIPEND_GAS, ESTIMATE_GAS_ERROR_RATIO}; use reth_storage_api::StateProvider; -use revm::context_interface::{result::ExecutionResult, Transaction}; +use revm::{ + context::Block, + context_interface::{result::ExecutionResult, Transaction}, +}; use tracing::trace; /// Gas execution estimates @@ -60,10 +63,10 @@ pub trait EstimateCall: Call { let tx_request_gas_limit = request.as_ref().gas_limit(); let tx_request_gas_price = request.as_ref().gas_price(); // the gas limit of the corresponding block - let max_gas_limit = evm_env - .cfg_env - .tx_gas_limit_cap - .map_or(evm_env.block_env.gas_limit, |cap| cap.min(evm_env.block_env.gas_limit)); + let max_gas_limit = evm_env.cfg_env.tx_gas_limit_cap.map_or_else( + || evm_env.block_env.gas_limit(), + |cap| cap.min(evm_env.block_env.gas_limit()), + ); // Determine the highest possible gas limit, considering both the request's specified limit // and the block's limit. diff --git a/crates/rpc/rpc-eth-api/src/helpers/mod.rs b/crates/rpc/rpc-eth-api/src/helpers/mod.rs index 29223d78913..19a72ccafb7 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/mod.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/mod.rs @@ -34,7 +34,7 @@ pub use call::{Call, EthCall}; pub use fee::{EthFees, LoadFee}; pub use pending_block::LoadPendingBlock; pub use receipt::LoadReceipt; -pub use signer::{AddDevSigners, EthSigner}; +pub use signer::EthSigner; pub use spec::EthApiSpec; pub use state::{EthState, LoadState}; pub use trace::Trace; diff --git a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs index 94dc214b6c8..1dda44d090e 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/pending_block.rs @@ -13,7 +13,7 @@ use reth_chainspec::{ChainSpecProvider, EthChainSpec}; use reth_errors::{BlockExecutionError, BlockValidationError, ProviderError, RethError}; use reth_evm::{ execute::{BlockBuilder, BlockBuilderOutcome, ExecutionOutcome}, - ConfigureEvm, Evm, NextBlockEnvAttributes, SpecFor, + ConfigureEvm, Evm, NextBlockEnvAttributes, }; use reth_primitives_traits::{transaction::error::InvalidTransactionError, HeaderTy, SealedHeader}; use reth_revm::{database::StateProviderDatabase, db::State}; @@ -23,8 +23,8 @@ use reth_rpc_eth_types::{ PendingBlockEnv, PendingBlockEnvOrigin, }; use reth_storage_api::{ - noop::NoopProvider, BlockReader, BlockReaderIdExt, ProviderBlock, ProviderHeader, - ProviderReceipt, ProviderTx, ReceiptProvider, StateProviderBox, StateProviderFactory, + noop::NoopProvider, BlockReader, BlockReaderIdExt, ProviderHeader, ProviderTx, ReceiptProvider, + StateProviderBox, StateProviderFactory, }; use reth_transaction_pool::{ error::InvalidPoolTransactionError, BestTransactions, BestTransactionsAttributes, @@ -61,17 +61,7 @@ pub trait LoadPendingBlock: /// Configures the [`PendingBlockEnv`] for the pending block /// /// If no pending block is available, this will derive it from the `latest` block - #[expect(clippy::type_complexity)] - fn pending_block_env_and_cfg( - &self, - ) -> Result< - PendingBlockEnv< - ProviderBlock, - ProviderReceipt, - SpecFor, - >, - Self::Error, - > { + fn pending_block_env_and_cfg(&self) -> Result, Self::Error> { if let Some(block) = self.provider().pending_block().map_err(Self::Error::from_eth_err)? && let Some(receipts) = self .provider() @@ -166,7 +156,7 @@ pub trait LoadPendingBlock: // Is the pending block cached? if let Some(pending_block) = lock.as_ref() { // Is the cached block not expired and latest is its parent? - if pending.evm_env.block_env.number == U256::from(pending_block.block().number()) && + if pending.evm_env.block_env.number() == U256::from(pending_block.block().number()) && parent.hash() == pending_block.block().parent_hash() && now <= pending_block.expires_at { @@ -265,14 +255,14 @@ pub trait LoadPendingBlock: .unwrap_or_else(BlobParams::cancun); let mut cumulative_gas_used = 0; let mut sum_blob_gas_used = 0; - let block_gas_limit: u64 = block_env.gas_limit; + let block_gas_limit: u64 = block_env.gas_limit(); // Only include transactions if not configured as Empty if !self.pending_block_kind().is_empty() { let mut best_txs = self .pool() .best_transactions_with_attributes(BestTransactionsAttributes::new( - block_env.basefee, + block_env.basefee(), block_env.blob_gasprice().map(|gasprice| gasprice as u64), )) // freeze to get a block as fast as possible @@ -369,7 +359,7 @@ pub trait LoadPendingBlock: } } - let BlockBuilderOutcome { execution_result, block, hashed_state, .. } = + let BlockBuilderOutcome { execution_result, block, hashed_state, trie_updates } = builder.finish(NoopProvider::default()).map_err(Self::Error::from_eth_err)?; let execution_outcome = ExecutionOutcome::new( @@ -383,6 +373,7 @@ pub trait LoadPendingBlock: recovered_block: block.into(), execution_output: Arc::new(execution_outcome), hashed_state: Arc::new(hashed_state), + trie_updates: Arc::new(trie_updates), }) } } diff --git a/crates/rpc/rpc-eth-api/src/helpers/signer.rs b/crates/rpc/rpc-eth-api/src/helpers/signer.rs index 4060be138e0..c54c8943c0a 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/signer.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/signer.rs @@ -32,11 +32,3 @@ pub trait EthSigner: Send + Sync + DynClone { } dyn_clone::clone_trait_object!( EthSigner); - -/// Adds 20 random dev signers for access via the API. Used in dev mode. -#[auto_impl::auto_impl(&)] -pub trait AddDevSigners { - /// Generates 20 random developer accounts. - /// Used in DEV mode. - fn with_dev_accounts(&self); -} diff --git a/crates/rpc/rpc-eth-api/src/helpers/trace.rs b/crates/rpc/rpc-eth-api/src/helpers/trace.rs index a3c79416cfe..86039e38082 100644 --- a/crates/rpc/rpc-eth-api/src/helpers/trace.rs +++ b/crates/rpc/rpc-eth-api/src/helpers/trace.rs @@ -19,14 +19,14 @@ use reth_rpc_eth_types::{ EthApiError, }; use reth_storage_api::{ProviderBlock, ProviderTx}; -use revm::{context_interface::result::ResultAndState, DatabaseCommit}; +use revm::{context::Block, context_interface::result::ResultAndState, DatabaseCommit}; use revm_inspectors::tracing::{TracingInspector, TracingInspectorConfig}; use std::sync::Arc; /// Executes CPU heavy tasks. pub trait Trace: LoadState> { - /// Executes the [`TxEnvFor`] with [`EvmEnvFor`] against the given [Database] without committing - /// state changes. + /// Executes the [`TxEnvFor`] with [`reth_evm::EvmEnv`] against the given [Database] without + /// committing state changes. fn inspect( &self, db: DB, @@ -301,8 +301,8 @@ pub trait Trace: LoadState> { let state_at = block.parent_hash(); let block_hash = block.hash(); - let block_number = evm_env.block_env.number.saturating_to(); - let base_fee = evm_env.block_env.basefee; + let block_number = evm_env.block_env.number().saturating_to(); + let base_fee = evm_env.block_env.basefee(); // now get the state let state = this.state_at_block_id(state_at.into()).await?; diff --git a/crates/rpc/rpc-eth-api/src/types.rs b/crates/rpc/rpc-eth-api/src/types.rs index 22100520016..ed4fcfa5c80 100644 --- a/crates/rpc/rpc-eth-api/src/types.rs +++ b/crates/rpc/rpc-eth-api/src/types.rs @@ -2,11 +2,9 @@ use crate::{AsEthApiError, FromEthApiError, RpcNodeCore}; use alloy_rpc_types_eth::Block; -use reth_chain_state::CanonStateSubscriptions; -use reth_rpc_convert::RpcConvert; +use reth_rpc_convert::{RpcConvert, SignableTxRequest}; pub use reth_rpc_convert::{RpcTransaction, RpcTxReq, RpcTypes}; -use reth_storage_api::{ProviderTx, ReceiptProvider, TransactionsProvider}; -use reth_transaction_pool::{PoolTransaction, TransactionPool}; +use reth_storage_api::ProviderTx; use std::{ error::Error, fmt::{self}, @@ -52,12 +50,11 @@ pub type RpcError = ::Error; /// Helper trait holds necessary trait bounds on [`EthApiTypes`] to implement `eth` API. pub trait FullEthApiTypes where - Self: RpcNodeCore< - Provider: TransactionsProvider + ReceiptProvider + CanonStateSubscriptions, - Pool: TransactionPool< - Transaction: PoolTransaction>, + Self: RpcNodeCore + + EthApiTypes< + NetworkTypes: RpcTypes< + TransactionRequest: SignableTxRequest>, >, - > + EthApiTypes< RpcConvert: RpcConvert< Primitives = Self::Primitives, Network = Self::NetworkTypes, @@ -68,12 +65,11 @@ where } impl FullEthApiTypes for T where - T: RpcNodeCore< - Provider: TransactionsProvider + ReceiptProvider + CanonStateSubscriptions, - Pool: TransactionPool< - Transaction: PoolTransaction>, + T: RpcNodeCore + + EthApiTypes< + NetworkTypes: RpcTypes< + TransactionRequest: SignableTxRequest>, >, - > + EthApiTypes< RpcConvert: RpcConvert< Primitives = ::Primitives, Network = Self::NetworkTypes, diff --git a/crates/rpc/rpc-eth-types/src/error/mod.rs b/crates/rpc/rpc-eth-types/src/error/mod.rs index 1f3ee7dd6dd..fdb5f8f190f 100644 --- a/crates/rpc/rpc-eth-types/src/error/mod.rs +++ b/crates/rpc/rpc-eth-types/src/error/mod.rs @@ -69,7 +69,7 @@ pub enum EthApiError { InvalidTransactionSignature, /// Errors related to the transaction pool #[error(transparent)] - PoolError(RpcPoolError), + PoolError(#[from] RpcPoolError), /// Header not found for block hash/number/tag #[error("header not found")] HeaderNotFound(BlockId), @@ -186,6 +186,16 @@ pub enum EthApiError { /// Error thrown when batch tx send channel fails #[error("Batch transaction sender channel closed")] BatchTxSendError, + /// Error that occurred during `call_many` execution with bundle and transaction context + #[error("call_many error in bundle {bundle_index} and transaction {tx_index}: {}", .error.message())] + CallManyError { + /// Bundle index where the error occurred + bundle_index: usize, + /// Transaction index within the bundle where the error occurred + tx_index: usize, + /// The underlying error object + error: jsonrpsee_types::ErrorObject<'static>, + }, /// Any other error #[error("{0}")] Other(Box), @@ -197,6 +207,15 @@ impl EthApiError { Self::Other(Box::new(err)) } + /// Creates a new [`EthApiError::CallManyError`] variant. + pub const fn call_many_error( + bundle_index: usize, + tx_index: usize, + error: jsonrpsee_types::ErrorObject<'static>, + ) -> Self { + Self::CallManyError { bundle_index, tx_index, error } + } + /// Returns `true` if error is [`RpcInvalidTransactionError::GasTooHigh`] pub const fn is_gas_too_high(&self) -> bool { matches!( @@ -304,6 +323,16 @@ impl From for jsonrpsee_types::error::ErrorObject<'static> { EthApiError::BatchTxSendError => { internal_rpc_err("Batch transaction sender channel closed".to_string()) } + EthApiError::CallManyError { bundle_index, tx_index, error } => { + jsonrpsee_types::error::ErrorObject::owned( + error.code(), + format!( + "call_many error in bundle {bundle_index} and transaction {tx_index}: {}", + error.message() + ), + error.data(), + ) + } } } } @@ -681,7 +710,7 @@ impl RpcInvalidTransactionError { /// Converts the halt error /// /// Takes the configured gas limit of the transaction which is attached to the error - pub const fn halt(reason: HaltReason, gas_limit: u64) -> Self { + pub fn halt(reason: HaltReason, gas_limit: u64) -> Self { match reason { HaltReason::OutOfGas(err) => Self::out_of_gas(err, gas_limit), HaltReason::NonceOverflow => Self::NonceMaxValue, @@ -762,7 +791,7 @@ impl From for RpcInvalidTransactionError { InvalidTransaction::BlobVersionedHashesNotSupported => { Self::BlobVersionedHashesNotSupported } - InvalidTransaction::BlobGasPriceGreaterThanMax => Self::BlobFeeCapTooLow, + InvalidTransaction::BlobGasPriceGreaterThanMax { .. } => Self::BlobFeeCapTooLow, InvalidTransaction::EmptyBlobs => Self::BlobTransactionMissingBlobHashes, InvalidTransaction::BlobVersionNotSupported => Self::BlobHashVersionMismatch, InvalidTransaction::TooManyBlobs { have, .. } => Self::TooManyBlobs { have }, @@ -780,6 +809,7 @@ impl From for RpcInvalidTransactionError { InvalidTransaction::Eip7873MissingTarget => { Self::other(internal_rpc_err(err.to_string())) } + InvalidTransaction::Str(_) => Self::other(internal_rpc_err(err.to_string())), } } } diff --git a/crates/rpc/rpc-eth-types/src/pending_block.rs b/crates/rpc/rpc-eth-types/src/pending_block.rs index 05ad6fb4e27..45f50ea82c5 100644 --- a/crates/rpc/rpc-eth-types/src/pending_block.rs +++ b/crates/rpc/rpc-eth-types/src/pending_block.rs @@ -9,22 +9,20 @@ use alloy_consensus::BlockHeader; use alloy_eips::{BlockId, BlockNumberOrTag}; use alloy_primitives::{BlockHash, B256}; use derive_more::Constructor; -use reth_chain_state::{ - BlockState, ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates, -}; +use reth_chain_state::{BlockState, ExecutedBlock}; use reth_ethereum_primitives::Receipt; -use reth_evm::EvmEnv; +use reth_evm::{ConfigureEvm, EvmEnvFor}; use reth_primitives_traits::{ Block, BlockTy, NodePrimitives, ReceiptTy, RecoveredBlock, SealedHeader, }; -/// Configured [`EvmEnv`] for a pending block. +/// Configured [`reth_evm::EvmEnv`] for a pending block. #[derive(Debug, Clone, Constructor)] -pub struct PendingBlockEnv { - /// Configured [`EvmEnv`] for the pending block. - pub evm_env: EvmEnv, +pub struct PendingBlockEnv { + /// Configured [`reth_evm::EvmEnv`] for the pending block. + pub evm_env: EvmEnvFor, /// Origin block for the config - pub origin: PendingBlockEnvOrigin, + pub origin: PendingBlockEnvOrigin, ReceiptTy>, } /// The origin for a configured [`PendingBlockEnv`] @@ -135,11 +133,6 @@ impl PendingBlock { impl From> for BlockState { fn from(pending_block: PendingBlock) -> Self { - Self::new(ExecutedBlockWithTrieUpdates::::new( - pending_block.executed_block.recovered_block, - pending_block.executed_block.execution_output, - pending_block.executed_block.hashed_state, - ExecutedTrieUpdates::Missing, - )) + Self::new(pending_block.executed_block) } } diff --git a/crates/rpc/rpc-eth-types/src/simulate.rs b/crates/rpc/rpc-eth-types/src/simulate.rs index 5492e127b77..ec63443da3d 100644 --- a/crates/rpc/rpc-eth-types/src/simulate.rs +++ b/crates/rpc/rpc-eth-types/src/simulate.rs @@ -24,6 +24,7 @@ use reth_rpc_convert::{RpcBlock, RpcConvert, RpcTxReq}; use reth_rpc_server_types::result::rpc_err; use reth_storage_api::noop::NoopProvider; use revm::{ + context::Block, context_interface::result::ExecutionResult, primitives::{Address, Bytes, TxKind}, Database, @@ -88,7 +89,7 @@ where let tx = resolve_transaction( call, default_gas_limit, - builder.evm().block().basefee, + builder.evm().block().basefee(), chain_id, builder.evm_mut().db_mut(), tx_resp_builder, diff --git a/crates/rpc/rpc/Cargo.toml b/crates/rpc/rpc/Cargo.toml index c47c383f057..e028e47448d 100644 --- a/crates/rpc/rpc/Cargo.toml +++ b/crates/rpc/rpc/Cargo.toml @@ -45,7 +45,7 @@ reth-trie-common.workspace = true alloy-evm = { workspace = true, features = ["overrides"] } alloy-consensus.workspace = true alloy-signer.workspace = true -alloy-signer-local.workspace = true +alloy-signer-local = { workspace = true, features = ["mnemonic"] } alloy-eips = { workspace = true, features = ["kzg"] } alloy-dyn-abi.workspace = true alloy-genesis.workspace = true diff --git a/crates/rpc/rpc/src/aliases.rs b/crates/rpc/rpc/src/aliases.rs index 4e317305ca4..8854f1b607d 100644 --- a/crates/rpc/rpc/src/aliases.rs +++ b/crates/rpc/rpc/src/aliases.rs @@ -1,4 +1,4 @@ -use reth_evm::{ConfigureEvm, SpecFor, TxEnvFor}; +use reth_evm::ConfigureEvm; use reth_rpc_convert::RpcConvert; use reth_rpc_eth_types::EthApiError; @@ -8,7 +8,6 @@ pub type DynRpcConverter = Box< Primitives = ::Primitives, Network = Network, Error = Error, - TxEnv = TxEnvFor, - Spec = SpecFor, + Evm = Evm, >, >; diff --git a/crates/rpc/rpc/src/debug.rs b/crates/rpc/rpc/src/debug.rs index b3715c0e8e0..066f7180c85 100644 --- a/crates/rpc/rpc/src/debug.rs +++ b/crates/rpc/rpc/src/debug.rs @@ -3,6 +3,7 @@ use alloy_consensus::{ BlockHeader, }; use alloy_eips::{eip2718::Encodable2718, BlockId, BlockNumberOrTag}; +use alloy_evm::env::BlockEnvironment; use alloy_genesis::ChainConfig; use alloy_primitives::{uint, Address, Bytes, B256}; use alloy_rlp::{Decodable, Encodable}; @@ -40,7 +41,7 @@ use reth_storage_api::{ }; use reth_tasks::pool::BlockingTaskGuard; use reth_trie_common::{updates::TrieUpdates, HashedPostState}; -use revm::{context_interface::Transaction, state::EvmState, DatabaseCommit}; +use revm::{context::Block, context_interface::Transaction, state::EvmState, DatabaseCommit}; use revm_inspectors::tracing::{ FourByteInspector, MuxInspector, TracingInspector, TracingInspectorConfig, TransactionContext, }; @@ -168,8 +169,6 @@ where .iter() .map(|tx| tx.recover_signer().map_err(Eth::Error::from_eth_err)) .collect::, _>>()? - .into_iter() - .collect() } else { block .body() @@ -177,8 +176,6 @@ where .iter() .map(|tx| tx.recover_signer_unchecked().map_err(Eth::Error::from_eth_err)) .collect::, _>>()? - .into_iter() - .collect() }; self.trace_block(Arc::new(block.into_recovered_with_signers(senders)), evm_env, opts).await @@ -372,8 +369,8 @@ where let db = db.0; let tx_info = TransactionInfo { - block_number: Some(evm_env.block_env.number.saturating_to()), - base_fee: Some(evm_env.block_env.basefee), + block_number: Some(evm_env.block_env.number().saturating_to()), + base_fee: Some(evm_env.block_env.basefee()), hash: None, block_hash: None, index: None, @@ -419,6 +416,11 @@ where Ok(frame.into()) } + _ => { + // Note: this match is non-exhaustive in case we need to add support for + // additional tracers + Err(EthApiError::Unsupported("unsupported tracer").into()) + } }, #[cfg(not(feature = "js-tracer"))] GethDebugTracerType::JsTracer(_) => { @@ -589,8 +591,8 @@ where results.push(trace); } // Increment block_env number and timestamp for the next bundle - evm_env.block_env.number += uint!(1_U256); - evm_env.block_env.timestamp += uint!(12_U256); + evm_env.block_env.inner_mut().number += uint!(1_U256); + evm_env.block_env.inner_mut().timestamp += uint!(12_U256); all_bundles.push(results); } @@ -741,8 +743,8 @@ where .map(|c| c.tx_index.map(|i| i as u64)) .unwrap_or_default(), block_hash: transaction_context.as_ref().map(|c| c.block_hash).unwrap_or_default(), - block_number: Some(evm_env.block_env.number.saturating_to()), - base_fee: Some(evm_env.block_env.basefee), + block_number: Some(evm_env.block_env.number().saturating_to()), + base_fee: Some(evm_env.block_env.basefee()), }; if let Some(tracer) = tracer { @@ -838,6 +840,11 @@ where return Ok((frame.into(), res.state)); } + _ => { + // Note: this match is non-exhaustive in case we need to add support for + // additional tracers + Err(EthApiError::Unsupported("unsupported tracer").into()) + } }, #[cfg(not(feature = "js-tracer"))] GethDebugTracerType::JsTracer(_) => { diff --git a/crates/rpc/rpc/src/engine.rs b/crates/rpc/rpc/src/engine.rs index a0e0bd30931..7865659ece7 100644 --- a/crates/rpc/rpc/src/engine.rs +++ b/crates/rpc/rpc/src/engine.rs @@ -16,7 +16,7 @@ use tracing_futures::Instrument; macro_rules! engine_span { () => { - tracing::trace_span!(target: "rpc", "engine") + tracing::info_span!(target: "rpc", "engine") }; } diff --git a/crates/rpc/rpc/src/eth/bundle.rs b/crates/rpc/rpc/src/eth/bundle.rs index 48e3219daa3..0797c2f1f8c 100644 --- a/crates/rpc/rpc/src/eth/bundle.rs +++ b/crates/rpc/rpc/src/eth/bundle.rs @@ -2,12 +2,12 @@ use alloy_consensus::{transaction::TxHashRef, EnvKzgSettings, Transaction as _}; use alloy_eips::eip7840::BlobParams; +use alloy_evm::env::BlockEnvironment; use alloy_primitives::{uint, Keccak256, U256}; use alloy_rpc_types_mev::{EthCallBundle, EthCallBundleResponse, EthCallBundleTransactionResult}; use jsonrpsee::core::RpcResult; use reth_chainspec::{ChainSpecProvider, EthChainSpec}; use reth_evm::{ConfigureEvm, Evm}; - use reth_revm::{database::StateProviderDatabase, db::CacheDB}; use reth_rpc_eth_api::{ helpers::{Call, EthTransactions, LoadPendingBlock}, @@ -18,7 +18,9 @@ use reth_tasks::pool::BlockingTaskGuard; use reth_transaction_pool::{ EthBlobTransactionSidecar, EthPoolTransaction, PoolPooledTx, PoolTransaction, TransactionPool, }; -use revm::{context_interface::result::ResultAndState, DatabaseCommit, DatabaseRef}; +use revm::{ + context::Block, context_interface::result::ResultAndState, DatabaseCommit, DatabaseRef, +}; use std::sync::Arc; /// `Eth` bundle implementation. @@ -88,18 +90,18 @@ where let (mut evm_env, at) = self.eth_api().evm_env_at(block_id).await?; if let Some(coinbase) = coinbase { - evm_env.block_env.beneficiary = coinbase; + evm_env.block_env.inner_mut().beneficiary = coinbase; } // need to adjust the timestamp for the next block if let Some(timestamp) = timestamp { - evm_env.block_env.timestamp = U256::from(timestamp); + evm_env.block_env.inner_mut().timestamp = U256::from(timestamp); } else { - evm_env.block_env.timestamp += uint!(12_U256); + evm_env.block_env.inner_mut().timestamp += uint!(12_U256); } if let Some(difficulty) = difficulty { - evm_env.block_env.difficulty = U256::from(difficulty); + evm_env.block_env.inner_mut().difficulty = U256::from(difficulty); } // Validate that the bundle does not contain more than MAX_BLOB_NUMBER_PER_BLOCK blob @@ -110,7 +112,7 @@ where .eth_api() .provider() .chain_spec() - .blob_params_at_timestamp(evm_env.block_env.timestamp.saturating_to()) + .blob_params_at_timestamp(evm_env.block_env.timestamp().saturating_to()) .unwrap_or_else(BlobParams::cancun); if transactions.iter().filter_map(|tx| tx.blob_gas_used()).sum::() > blob_params.max_blob_gas_per_block() @@ -124,30 +126,30 @@ where } // default to call gas limit unless user requests a smaller limit - evm_env.block_env.gas_limit = self.inner.eth_api.call_gas_limit(); + evm_env.block_env.inner_mut().gas_limit = self.inner.eth_api.call_gas_limit(); if let Some(gas_limit) = gas_limit { - if gas_limit > evm_env.block_env.gas_limit { + if gas_limit > evm_env.block_env.gas_limit() { return Err( EthApiError::InvalidTransaction(RpcInvalidTransactionError::GasTooHigh).into() ) } - evm_env.block_env.gas_limit = gas_limit; + evm_env.block_env.inner_mut().gas_limit = gas_limit; } if let Some(base_fee) = base_fee { - evm_env.block_env.basefee = base_fee.try_into().unwrap_or(u64::MAX); + evm_env.block_env.inner_mut().basefee = base_fee.try_into().unwrap_or(u64::MAX); } - let state_block_number = evm_env.block_env.number; + let state_block_number = evm_env.block_env.number(); // use the block number of the request - evm_env.block_env.number = U256::from(block_number); + evm_env.block_env.inner_mut().number = U256::from(block_number); let eth_api = self.eth_api().clone(); self.eth_api() .spawn_with_state_at_block(at, move |state| { - let coinbase = evm_env.block_env.beneficiary; - let basefee = evm_env.block_env.basefee; + let coinbase = evm_env.block_env.beneficiary(); + let basefee = evm_env.block_env.basefee(); let db = CacheDB::new(StateProviderDatabase::new(state)); let initial_coinbase = db diff --git a/crates/rpc/rpc/src/eth/core.rs b/crates/rpc/rpc/src/eth/core.rs index 61082f4f929..e3850a67f54 100644 --- a/crates/rpc/rpc/src/eth/core.rs +++ b/crates/rpc/rpc/src/eth/core.rs @@ -30,8 +30,8 @@ use reth_tasks::{ TaskSpawner, TokioTaskExecutor, }; use reth_transaction_pool::{ - noop::NoopTransactionPool, AddedTransactionOutcome, BatchTxProcessor, BatchTxRequest, - TransactionPool, + blobstore::BlobSidecarConverter, noop::NoopTransactionPool, AddedTransactionOutcome, + BatchTxProcessor, BatchTxRequest, TransactionPool, }; use tokio::sync::{broadcast, mpsc, Mutex}; @@ -315,6 +315,9 @@ pub struct EthApiInner { /// Timeout duration for `send_raw_transaction_sync` RPC method. send_raw_transaction_sync_timeout: Duration, + + /// Blob sidecar converter + blob_sidecar_converter: BlobSidecarConverter, } impl EthApiInner @@ -382,6 +385,7 @@ where tx_batch_sender, pending_block_kind, send_raw_transaction_sync_timeout, + blob_sidecar_converter: BlobSidecarConverter::new(), } } } @@ -553,6 +557,12 @@ where pub const fn send_raw_transaction_sync_timeout(&self) -> Duration { self.send_raw_transaction_sync_timeout } + + /// Returns a handle to the blob sidecar converter. + #[inline] + pub const fn blob_sidecar_converter(&self) -> &BlobSidecarConverter { + &self.blob_sidecar_converter + } } #[cfg(test)] diff --git a/crates/rpc/rpc/src/eth/helpers/call.rs b/crates/rpc/rpc/src/eth/helpers/call.rs index a76e146042d..abe06cb55ec 100644 --- a/crates/rpc/rpc/src/eth/helpers/call.rs +++ b/crates/rpc/rpc/src/eth/helpers/call.rs @@ -1,7 +1,6 @@ //! Contains RPC handler implementations specific to endpoints that call/execute within evm. use crate::EthApi; -use reth_evm::{SpecFor, TxEnvFor}; use reth_rpc_convert::RpcConvert; use reth_rpc_eth_api::{ helpers::{estimate::EstimateCall, Call, EthCall}, @@ -13,12 +12,7 @@ impl EthCall for EthApi where N: RpcNodeCore, EthApiError: FromEvmError, - Rpc: RpcConvert< - Primitives = N::Primitives, - Error = EthApiError, - TxEnv = TxEnvFor, - Spec = SpecFor, - >, + Rpc: RpcConvert, { } @@ -26,12 +20,7 @@ impl Call for EthApi where N: RpcNodeCore, EthApiError: FromEvmError, - Rpc: RpcConvert< - Primitives = N::Primitives, - Error = EthApiError, - TxEnv = TxEnvFor, - Spec = SpecFor, - >, + Rpc: RpcConvert, { #[inline] fn call_gas_limit(&self) -> u64 { @@ -48,11 +37,6 @@ impl EstimateCall for EthApi where N: RpcNodeCore, EthApiError: FromEvmError, - Rpc: RpcConvert< - Primitives = N::Primitives, - Error = EthApiError, - TxEnv = TxEnvFor, - Spec = SpecFor, - >, + Rpc: RpcConvert, { } diff --git a/crates/rpc/rpc/src/eth/helpers/signer.rs b/crates/rpc/rpc/src/eth/helpers/signer.rs index 60d6a151f9b..2c18245d542 100644 --- a/crates/rpc/rpc/src/eth/helpers/signer.rs +++ b/crates/rpc/rpc/src/eth/helpers/signer.rs @@ -1,33 +1,14 @@ //! An abstraction over ethereum signers. -use std::collections::HashMap; - -use crate::EthApi; use alloy_dyn_abi::TypedData; use alloy_eips::eip2718::Decodable2718; use alloy_primitives::{eip191_hash_message, Address, Signature, B256}; use alloy_signer::SignerSync; -use alloy_signer_local::PrivateKeySigner; -use reth_rpc_convert::{RpcConvert, RpcTypes, SignableTxRequest}; -use reth_rpc_eth_api::{ - helpers::{signer::Result, AddDevSigners, EthSigner}, - FromEvmError, RpcNodeCore, -}; -use reth_rpc_eth_types::{EthApiError, SignError}; -use reth_storage_api::ProviderTx; - -impl AddDevSigners for EthApi -where - N: RpcNodeCore, - EthApiError: FromEvmError, - Rpc: RpcConvert< - Network: RpcTypes>>, - >, -{ - fn with_dev_accounts(&self) { - *self.inner.signers().write() = DevSigner::random_signers(20) - } -} +use alloy_signer_local::{coins_bip39::English, MnemonicBuilder, PrivateKeySigner}; +use reth_rpc_convert::SignableTxRequest; +use reth_rpc_eth_api::helpers::{signer::Result, EthSigner}; +use reth_rpc_eth_types::SignError; +use std::collections::HashMap; /// Holds developer keys #[derive(Debug, Clone)] @@ -55,6 +36,32 @@ impl DevSigner { signers } + /// Generates dev signers deterministically from a fixed mnemonic. + /// Uses the Ethereum derivation path: `m/44'/60'/0'/0/{index}` + pub fn from_mnemonic>( + mnemonic: &str, + num: u32, + ) -> Vec + 'static>> { + let mut signers = Vec::with_capacity(num as usize); + + for i in 0..num { + let sk = MnemonicBuilder::::default() + .phrase(mnemonic) + .index(i) + .expect("invalid derivation path") + .build() + .expect("failed to build signer from mnemonic"); + + let address = sk.address(); + let addresses = vec![address]; + let accounts = HashMap::from([(address, sk)]); + + signers.push(Box::new(Self { addresses, accounts }) as Box>); + } + + signers + } + fn get_key(&self, account: Address) -> Result<&PrivateKeySigner> { self.accounts.get(&account).ok_or(SignError::NoAccount) } diff --git a/crates/rpc/rpc/src/eth/helpers/transaction.rs b/crates/rpc/rpc/src/eth/helpers/transaction.rs index 4fa39112166..39758f68d77 100644 --- a/crates/rpc/rpc/src/eth/helpers/transaction.rs +++ b/crates/rpc/rpc/src/eth/helpers/transaction.rs @@ -3,14 +3,22 @@ use std::time::Duration; use crate::EthApi; +use alloy_consensus::BlobTransactionValidationError; +use alloy_eips::{eip7594::BlobTransactionSidecarVariant, BlockId, Typed2718}; use alloy_primitives::{hex, Bytes, B256}; +use reth_chainspec::{ChainSpecProvider, EthereumHardforks}; +use reth_primitives_traits::AlloyBlockHeader; use reth_rpc_convert::RpcConvert; use reth_rpc_eth_api::{ helpers::{spec::SignersForRpc, EthTransactions, LoadTransaction}, FromEvmError, RpcNodeCore, }; -use reth_rpc_eth_types::{utils::recover_raw_transaction, EthApiError}; -use reth_transaction_pool::{AddedTransactionOutcome, PoolTransaction, TransactionPool}; +use reth_rpc_eth_types::{error::RpcPoolError, utils::recover_raw_transaction, EthApiError}; +use reth_storage_api::BlockReaderIdExt; +use reth_transaction_pool::{ + error::Eip4844PoolTransactionError, AddedTransactionOutcome, EthBlobTransactionSidecar, + EthPoolTransaction, PoolTransaction, TransactionPool, +}; impl EthTransactions for EthApi where @@ -34,7 +42,56 @@ where async fn send_raw_transaction(&self, tx: Bytes) -> Result { let recovered = recover_raw_transaction(&tx)?; - let pool_transaction = ::Transaction::from_pooled(recovered); + let mut pool_transaction = + ::Transaction::from_pooled(recovered); + + // TODO: remove this after Osaka transition + // Convert legacy blob sidecars to EIP-7594 format + if pool_transaction.is_eip4844() { + let EthBlobTransactionSidecar::Present(sidecar) = pool_transaction.take_blob() else { + return Err(EthApiError::PoolError(RpcPoolError::Eip4844( + Eip4844PoolTransactionError::MissingEip4844BlobSidecar, + ))); + }; + + let sidecar = match sidecar { + BlobTransactionSidecarVariant::Eip4844(sidecar) => { + let latest = self + .provider() + .latest_header()? + .ok_or(EthApiError::HeaderNotFound(BlockId::latest()))?; + // Convert to EIP-7594 if next block is Osaka + if self + .provider() + .chain_spec() + .is_osaka_active_at_timestamp(latest.timestamp().saturating_add(12)) + { + BlobTransactionSidecarVariant::Eip7594( + self.blob_sidecar_converter().convert(sidecar).await.ok_or_else( + || { + RpcPoolError::Eip4844( + Eip4844PoolTransactionError::InvalidEip4844Blob( + BlobTransactionValidationError::InvalidProof, + ), + ) + }, + )?, + ) + } else { + BlobTransactionSidecarVariant::Eip4844(sidecar) + } + } + sidecar => sidecar, + }; + + pool_transaction = + EthPoolTransaction::try_from_eip4844(pool_transaction.into_consensus(), sidecar) + .ok_or_else(|| { + RpcPoolError::Eip4844( + Eip4844PoolTransactionError::MissingEip4844BlobSidecar, + ) + })?; + } // forward the transaction to the specific endpoint if configured. if let Some(client) = self.raw_tx_forwarder() { diff --git a/crates/rpc/rpc/src/eth/pubsub.rs b/crates/rpc/rpc/src/eth/pubsub.rs index 1c7982f80fd..985cdf3129e 100644 --- a/crates/rpc/rpc/src/eth/pubsub.rs +++ b/crates/rpc/rpc/src/eth/pubsub.rs @@ -101,6 +101,7 @@ where kind: SubscriptionKind, params: Option, ) -> Result<(), ErrorObject<'static>> { + #[allow(unreachable_patterns)] match kind { SubscriptionKind::NewHeads => { pipe_from_stream(accepted_sink, self.new_headers_stream()).await @@ -199,6 +200,10 @@ where Ok(()) } + _ => { + // TODO: implement once https://github.com/alloy-rs/alloy/pull/2974 is released + Err(invalid_params_rpc_err("Unsupported subscription kind")) + } } } } diff --git a/crates/rpc/rpc/src/eth/sim_bundle.rs b/crates/rpc/rpc/src/eth/sim_bundle.rs index f7043821754..328ea29193f 100644 --- a/crates/rpc/rpc/src/eth/sim_bundle.rs +++ b/crates/rpc/rpc/src/eth/sim_bundle.rs @@ -2,7 +2,7 @@ use alloy_consensus::{transaction::TxHashRef, BlockHeader}; use alloy_eips::BlockNumberOrTag; -use alloy_evm::overrides::apply_block_overrides; +use alloy_evm::{env::BlockEnvironment, overrides::apply_block_overrides}; use alloy_primitives::U256; use alloy_rpc_types_eth::BlockId; use alloy_rpc_types_mev::{ @@ -22,7 +22,9 @@ use reth_rpc_eth_types::{utils::recover_raw_transaction, EthApiError}; use reth_storage_api::ProviderTx; use reth_tasks::pool::BlockingTaskGuard; use reth_transaction_pool::{PoolPooledTx, PoolTransaction, TransactionPool}; -use revm::{context_interface::result::ResultAndState, DatabaseCommit, DatabaseRef}; +use revm::{ + context::Block, context_interface::result::ResultAndState, DatabaseCommit, DatabaseRef, +}; use std::{sync::Arc, time::Duration}; use tracing::trace; @@ -242,12 +244,12 @@ where .spawn_with_state_at_block(current_block_id, move |state| { // Setup environment let current_block_number = current_block.number(); - let coinbase = evm_env.block_env.beneficiary; - let basefee = evm_env.block_env.basefee; + let coinbase = evm_env.block_env.beneficiary(); + let basefee = evm_env.block_env.basefee(); let mut db = CacheDB::new(StateProviderDatabase::new(state)); // apply overrides - apply_block_overrides(block_overrides, &mut db, &mut evm_env.block_env); + apply_block_overrides(block_overrides, &mut db, evm_env.block_env.inner_mut()); let initial_coinbase_balance = DatabaseRef::basic_ref(&db, coinbase) .map_err(EthApiError::from_eth_err)? @@ -424,7 +426,7 @@ where let timeout = override_timeout .map(Duration::from_secs) - .filter(|&custom_duration| custom_duration <= MAX_SIM_TIMEOUT) + .map(|d| d.min(MAX_SIM_TIMEOUT)) .unwrap_or(DEFAULT_SIM_TIMEOUT); let bundle_res = diff --git a/crates/stages/api/src/pipeline/mod.rs b/crates/stages/api/src/pipeline/mod.rs index 2446219ea3d..ac35a489031 100644 --- a/crates/stages/api/src/pipeline/mod.rs +++ b/crates/stages/api/src/pipeline/mod.rs @@ -639,14 +639,18 @@ impl Pipeline { // FIXME: When handling errors, we do not commit the database transaction. This // leads to the Merkle stage not clearing its checkpoint, and restarting from an // invalid place. - let provider_rw = self.provider_factory.database_provider_rw()?; - provider_rw.save_stage_checkpoint_progress(StageId::MerkleExecute, vec![])?; - provider_rw.save_stage_checkpoint( - StageId::MerkleExecute, - prev_checkpoint.unwrap_or_default(), - )?; + // Only reset MerkleExecute checkpoint if MerkleExecute itself failed + if stage_id == StageId::MerkleExecute { + let provider_rw = self.provider_factory.database_provider_rw()?; + provider_rw + .save_stage_checkpoint_progress(StageId::MerkleExecute, vec![])?; + provider_rw.save_stage_checkpoint( + StageId::MerkleExecute, + prev_checkpoint.unwrap_or_default(), + )?; - provider_rw.commit()?; + provider_rw.commit()?; + } // We unwind because of a validation error. If the unwind itself // fails, we bail entirely, diff --git a/crates/stages/stages/benches/setup/mod.rs b/crates/stages/stages/benches/setup/mod.rs index bd1fb59ebe9..01d7571e0da 100644 --- a/crates/stages/stages/benches/setup/mod.rs +++ b/crates/stages/stages/benches/setup/mod.rs @@ -165,7 +165,7 @@ pub(crate) fn txs_testdata(num_blocks: u64) -> TestStageDB { db.insert_changesets(transitions, None).unwrap(); let provider_rw = db.factory.provider_rw().unwrap(); - provider_rw.write_trie_updates(&updates).unwrap(); + provider_rw.write_trie_updates(updates).unwrap(); provider_rw.commit().unwrap(); let (transitions, final_state) = random_changeset_range( diff --git a/crates/stages/stages/src/sets.rs b/crates/stages/stages/src/sets.rs index 97c3a3116aa..015be507336 100644 --- a/crates/stages/stages/src/sets.rs +++ b/crates/stages/stages/src/sets.rs @@ -39,9 +39,9 @@ use crate::{ stages::{ AccountHashingStage, BodyStage, EraImportSource, EraStage, ExecutionStage, FinishStage, - HeaderStage, IndexAccountHistoryStage, IndexStorageHistoryStage, MerkleStage, - PruneSenderRecoveryStage, PruneStage, SenderRecoveryStage, StorageHashingStage, - TransactionLookupStage, + HeaderStage, IndexAccountHistoryStage, IndexStorageHistoryStage, MerkleChangeSets, + MerkleStage, PruneSenderRecoveryStage, PruneStage, SenderRecoveryStage, + StorageHashingStage, TransactionLookupStage, }, StageSet, StageSetBuilder, }; @@ -54,7 +54,7 @@ use reth_primitives_traits::{Block, NodePrimitives}; use reth_provider::HeaderSyncGapProvider; use reth_prune_types::PruneModes; use reth_stages_api::Stage; -use std::{ops::Not, sync::Arc}; +use std::sync::Arc; use tokio::sync::watch; /// A set containing all stages to run a fully syncing instance of reth. @@ -75,6 +75,7 @@ use tokio::sync::watch; /// - [`AccountHashingStage`] /// - [`StorageHashingStage`] /// - [`MerkleStage`] (execute) +/// - [`MerkleChangeSets`] /// - [`TransactionLookupStage`] /// - [`IndexStorageHistoryStage`] /// - [`IndexAccountHistoryStage`] @@ -336,12 +337,12 @@ where stages_config: self.stages_config.clone(), prune_modes: self.prune_modes.clone(), }) - // If any prune modes are set, add the prune stage. - .add_stage_opt(self.prune_modes.is_empty().not().then(|| { - // Prune stage should be added after all hashing stages, because otherwise it will - // delete - PruneStage::new(self.prune_modes.clone(), self.stages_config.prune.commit_threshold) - })) + // Prune stage should be added after all hashing stages, because otherwise it will + // delete + .add_stage(PruneStage::new( + self.prune_modes.clone(), + self.stages_config.prune.commit_threshold, + )) } } @@ -387,6 +388,13 @@ where } /// A set containing all stages that hash account state. +/// +/// This includes: +/// - [`MerkleStage`] (unwind) +/// - [`AccountHashingStage`] +/// - [`StorageHashingStage`] +/// - [`MerkleStage`] (execute) +/// - [`MerkleChangeSets`] #[derive(Debug, Default)] #[non_exhaustive] pub struct HashingStages { @@ -399,6 +407,7 @@ where MerkleStage: Stage, AccountHashingStage: Stage, StorageHashingStage: Stage, + MerkleChangeSets: Stage, { fn builder(self) -> StageSetBuilder { StageSetBuilder::default() @@ -415,6 +424,7 @@ where self.stages_config.merkle.rebuild_threshold, self.stages_config.merkle.incremental_threshold, )) + .add_stage(MerkleChangeSets::new()) } } diff --git a/crates/stages/stages/src/stages/execution.rs b/crates/stages/stages/src/stages/execution.rs index 3736fa523cb..ed50572d58b 100644 --- a/crates/stages/stages/src/stages/execution.rs +++ b/crates/stages/stages/src/stages/execution.rs @@ -896,7 +896,7 @@ mod tests { // If there is a pruning configuration, then it's forced to use the database. // This way we test both cases. - let modes = [None, Some(PruneModes::none())]; + let modes = [None, Some(PruneModes::default())]; let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([( Address::random(), PruneMode::Distance(100000), @@ -1033,7 +1033,7 @@ mod tests { // If there is a pruning configuration, then it's forced to use the database. // This way we test both cases. - let modes = [None, Some(PruneModes::none())]; + let modes = [None, Some(PruneModes::default())]; let random_filter = ReceiptsLogPruneConfig(BTreeMap::from([( Address::random(), PruneMode::Before(100000), diff --git a/crates/stages/stages/src/stages/merkle.rs b/crates/stages/stages/src/stages/merkle.rs index 6cbed3ab20e..b4f24db7c58 100644 --- a/crates/stages/stages/src/stages/merkle.rs +++ b/crates/stages/stages/src/stages/merkle.rs @@ -247,7 +247,7 @@ where })?; match progress { StateRootProgress::Progress(state, hashed_entries_walked, updates) => { - provider.write_trie_updates(&updates)?; + provider.write_trie_updates(updates)?; let mut checkpoint = MerkleCheckpoint::new( to_block, @@ -290,7 +290,7 @@ where }) } StateRootProgress::Complete(root, hashed_entries_walked, updates) => { - provider.write_trie_updates(&updates)?; + provider.write_trie_updates(updates)?; entities_checkpoint.processed += hashed_entries_walked as u64; @@ -317,7 +317,7 @@ where error!(target: "sync::stages::merkle", %e, ?current_block_number, ?to_block, "Incremental state root failed! {INVALID_STATE_ROOT_ERROR_MESSAGE}"); StageError::Fatal(Box::new(e)) })?; - provider.write_trie_updates(&updates)?; + provider.write_trie_updates(updates)?; final_root = Some(root); } @@ -400,7 +400,7 @@ where validate_state_root(block_root, SealedHeader::seal_slow(target), input.unwind_to)?; // Validation passed, apply unwind changes to the database. - provider.write_trie_updates(&updates)?; + provider.write_trie_updates(updates)?; // Update entities checkpoint to reflect the unwind operation // Since we're unwinding, we need to recalculate the total entities at the target block diff --git a/crates/stages/stages/src/stages/merkle_changesets.rs b/crates/stages/stages/src/stages/merkle_changesets.rs new file mode 100644 index 00000000000..7bf756c3dd3 --- /dev/null +++ b/crates/stages/stages/src/stages/merkle_changesets.rs @@ -0,0 +1,380 @@ +use crate::stages::merkle::INVALID_STATE_ROOT_ERROR_MESSAGE; +use alloy_consensus::BlockHeader; +use alloy_primitives::BlockNumber; +use reth_consensus::ConsensusError; +use reth_primitives_traits::{GotExpected, SealedHeader}; +use reth_provider::{ + ChainStateBlockReader, DBProvider, HeaderProvider, ProviderError, StageCheckpointReader, + TrieWriter, +}; +use reth_stages_api::{ + BlockErrorKind, CheckpointBlockRange, ExecInput, ExecOutput, MerkleChangeSetsCheckpoint, Stage, + StageCheckpoint, StageError, StageId, UnwindInput, UnwindOutput, +}; +use reth_trie::{updates::TrieUpdates, HashedPostState, KeccakKeyHasher, StateRoot, TrieInput}; +use reth_trie_db::{DatabaseHashedPostState, DatabaseStateRoot}; +use std::ops::Range; +use tracing::{debug, error}; + +/// The `MerkleChangeSets` stage. +/// +/// This stage processes and maintains trie changesets from the finalized block to the latest block. +#[derive(Debug, Clone)] +pub struct MerkleChangeSets { + /// The number of blocks to retain changesets for, used as a fallback when the finalized block + /// is not found. Defaults to 64 (2 epochs in beacon chain). + retention_blocks: u64, +} + +impl MerkleChangeSets { + /// Creates a new `MerkleChangeSets` stage with default retention blocks of 64. + pub const fn new() -> Self { + Self { retention_blocks: 64 } + } + + /// Creates a new `MerkleChangeSets` stage with a custom finalized block height. + pub const fn with_retention_blocks(retention_blocks: u64) -> Self { + Self { retention_blocks } + } + + /// Returns the range of blocks which are already computed. Will return an empty range if none + /// have been computed. + fn computed_range(checkpoint: Option) -> Range { + let to = checkpoint.map(|chk| chk.block_number).unwrap_or_default(); + let from = checkpoint + .map(|chk| chk.merkle_changesets_stage_checkpoint().unwrap_or_default()) + .unwrap_or_default() + .block_range + .to; + from..to + 1 + } + + /// Determines the target range for changeset computation based on the checkpoint and provider + /// state. + /// + /// Returns the target range (exclusive end) to compute changesets for. + fn determine_target_range( + &self, + provider: &Provider, + ) -> Result, StageError> + where + Provider: StageCheckpointReader + ChainStateBlockReader, + { + // Get merkle checkpoint which represents our target end block + let merkle_checkpoint = provider + .get_stage_checkpoint(StageId::MerkleExecute)? + .map(|checkpoint| checkpoint.block_number) + .unwrap_or(0); + + let target_end = merkle_checkpoint + 1; // exclusive + + // Calculate the target range based on the finalized block and the target block. + // We maintain changesets from the finalized block to the latest block. + let finalized_block = provider.last_finalized_block_number()?; + + // Calculate the fallback start position based on retention blocks + let retention_based_start = merkle_checkpoint.saturating_sub(self.retention_blocks); + + // If the finalized block was way in the past then we don't want to generate changesets for + // all of those past blocks; we only care about the recent history. + // + // Use maximum of finalized_block and retention_based_start if finalized_block exists, + // otherwise just use retention_based_start. + let mut target_start = finalized_block + .map(|finalized| finalized.saturating_add(1).max(retention_based_start)) + .unwrap_or(retention_based_start); + + // We cannot revert the genesis block; target_start must be >0 + target_start = target_start.max(1); + + Ok(target_start..target_end) + } + + /// Calculates the trie updates given a [`TrieInput`], asserting that the resulting state root + /// matches the expected one for the block. + fn calculate_block_trie_updates( + provider: &Provider, + block_number: BlockNumber, + input: TrieInput, + ) -> Result { + let (root, trie_updates) = + StateRoot::overlay_root_from_nodes_with_updates(provider.tx_ref(), input).map_err( + |e| { + error!( + target: "sync::stages::merkle_changesets", + %e, + ?block_number, + "Incremental state root failed! {INVALID_STATE_ROOT_ERROR_MESSAGE}"); + StageError::Fatal(Box::new(e)) + }, + )?; + + let block = provider + .header_by_number(block_number)? + .ok_or_else(|| ProviderError::HeaderNotFound(block_number.into()))?; + + let (got, expected) = (root, block.state_root()); + if got != expected { + // Only seal the header when we need it for the error + let header = SealedHeader::seal_slow(block); + error!( + target: "sync::stages::merkle_changesets", + ?block_number, + ?got, + ?expected, + "Failed to verify block state root! {INVALID_STATE_ROOT_ERROR_MESSAGE}", + ); + return Err(StageError::Block { + error: BlockErrorKind::Validation(ConsensusError::BodyStateRootDiff( + GotExpected { got, expected }.into(), + )), + block: Box::new(header.block_with_parent()), + }) + } + + Ok(trie_updates) + } + + fn populate_range( + provider: &Provider, + target_range: Range, + ) -> Result<(), StageError> + where + Provider: StageCheckpointReader + + TrieWriter + + DBProvider + + HeaderProvider + + ChainStateBlockReader, + { + let target_start = target_range.start; + let target_end = target_range.end; + debug!( + target: "sync::stages::merkle_changesets", + ?target_range, + "Starting trie changeset computation", + ); + + // We need to distinguish a cumulative revert and a per-block revert. A cumulative revert + // reverts changes starting at db tip all the way to a block. A per-block revert only + // reverts a block's changes. + // + // We need to calculate the cumulative HashedPostState reverts for every block in the + // target range. The cumulative HashedPostState revert for block N can be calculated as: + // + // + // ``` + // // where `extend` overwrites any shared keys + // cumulative_state_revert(N) = cumulative_state_revert(N + 1).extend(get_block_state_revert(N)) + // ``` + // + // We need per-block reverts to calculate the prefix set for each individual block. By + // using the per-block reverts to calculate cumulative reverts on-the-fly we can save a + // bunch of memory. + debug!( + target: "sync::stages::merkle_changesets", + ?target_range, + "Computing per-block state reverts", + ); + let mut per_block_state_reverts = Vec::new(); + for block_number in target_range.clone() { + per_block_state_reverts.push(HashedPostState::from_reverts::( + provider.tx_ref(), + block_number..=block_number, + )?); + } + + // Helper to retrieve state revert data for a specific block from the pre-computed array + let get_block_state_revert = |block_number: BlockNumber| -> &HashedPostState { + let index = (block_number - target_start) as usize; + &per_block_state_reverts[index] + }; + + // Helper to accumulate state reverts from a given block to the target end + let compute_cumulative_state_revert = |block_number: BlockNumber| -> HashedPostState { + let mut cumulative_revert = HashedPostState::default(); + for n in (block_number..target_end).rev() { + cumulative_revert.extend_ref(get_block_state_revert(n)) + } + cumulative_revert + }; + + // To calculate the changeset for a block, we first need the TrieUpdates which are + // generated as a result of processing the block. To get these we need: + // 1) The TrieUpdates which revert the db's trie to _prior_ to the block + // 2) The HashedPostState to revert the db's state to _after_ the block + // + // To get (1) for `target_start` we need to do a big state root calculation which takes + // into account all changes between that block and db tip. For each block after the + // `target_start` we can update (1) using the TrieUpdates which were output by the previous + // block, only targeting the state changes of that block. + debug!( + target: "sync::stages::merkle_changesets", + ?target_start, + "Computing trie state at starting block", + ); + let mut input = TrieInput::default(); + input.state = compute_cumulative_state_revert(target_start); + input.prefix_sets = input.state.construct_prefix_sets(); + // target_start will be >= 1, see `determine_target_range`. + input.nodes = + Self::calculate_block_trie_updates(provider, target_start - 1, input.clone())?; + + for block_number in target_range { + debug!( + target: "sync::stages::merkle_changesets", + ?block_number, + "Computing trie updates for block", + ); + // Revert the state so that this block has been just processed, meaning we take the + // cumulative revert of the subsequent block. + input.state = compute_cumulative_state_revert(block_number + 1); + + // Construct prefix sets from only this block's `HashedPostState`, because we only care + // about trie updates which occurred as a result of this block being processed. + input.prefix_sets = get_block_state_revert(block_number).construct_prefix_sets(); + + // Calculate the trie updates for this block, then apply those updates to the reverts. + // We calculate the overlay which will be passed into the next step using the trie + // reverts prior to them being updated. + let this_trie_updates = + Self::calculate_block_trie_updates(provider, block_number, input.clone())?; + + let trie_overlay = input.nodes.clone().into_sorted(); + input.nodes.extend_ref(&this_trie_updates); + let this_trie_updates = this_trie_updates.into_sorted(); + + // Write the changesets to the DB using the trie updates produced by the block, and the + // trie reverts as the overlay. + debug!( + target: "sync::stages::merkle_changesets", + ?block_number, + "Writing trie changesets for block", + ); + provider.write_trie_changesets( + block_number, + &this_trie_updates, + Some(&trie_overlay), + )?; + } + + Ok(()) + } +} + +impl Default for MerkleChangeSets { + fn default() -> Self { + Self::new() + } +} + +impl Stage for MerkleChangeSets +where + Provider: + StageCheckpointReader + TrieWriter + DBProvider + HeaderProvider + ChainStateBlockReader, +{ + fn id(&self) -> StageId { + StageId::MerkleChangeSets + } + + fn execute(&mut self, provider: &Provider, input: ExecInput) -> Result { + // Get merkle checkpoint and assert that the target is the same. + let merkle_checkpoint = provider + .get_stage_checkpoint(StageId::MerkleExecute)? + .map(|checkpoint| checkpoint.block_number) + .unwrap_or(0); + + if input.target.is_none_or(|target| merkle_checkpoint != target) { + return Err(StageError::Fatal(eyre::eyre!("Cannot sync stage to block {:?} when MerkleExecute is at block {merkle_checkpoint:?}", input.target).into())) + } + + let mut target_range = self.determine_target_range(provider)?; + + // Get the previously computed range. This will be updated to reflect the populating of the + // target range. + let mut computed_range = Self::computed_range(input.checkpoint); + + // We want the target range to not include any data already computed previously, if + // possible, so we start the target range from the end of the computed range if that is + // greater. + // + // ------------------------------> Block # + // |------computed-----| + // |-----target-----| + // |--actual--| + // + // However, if the target start is less than the previously computed start, we don't want to + // do this, as it would leave a gap of data at `target_range.start..=computed_range.start`. + // + // ------------------------------> Block # + // |---computed---| + // |-------target-------| + // |-------actual-------| + // + if target_range.start >= computed_range.start { + target_range.start = target_range.start.max(computed_range.end); + } + + // If target range is empty (target_start >= target_end), stage is already successfully + // executed + if target_range.start >= target_range.end { + return Ok(ExecOutput::done(input.checkpoint.unwrap_or_default())); + } + + // If our target range is a continuation of the already computed range then we can keep the + // already computed data. + if target_range.start == computed_range.end { + // Clear from target_start onwards to ensure no stale data exists + provider.clear_trie_changesets_from(target_range.start)?; + computed_range.end = target_range.end; + } else { + // If our target range is not a continuation of the already computed range then we + // simply clear the computed data, to make sure there's no gaps or conflicts. + provider.clear_trie_changesets()?; + computed_range = target_range.clone(); + } + + // Populate the target range with changesets + Self::populate_range(provider, target_range)?; + + let checkpoint_block_range = CheckpointBlockRange { + from: computed_range.start, + // CheckpointBlockRange is inclusive + to: computed_range.end.saturating_sub(1), + }; + + let checkpoint = StageCheckpoint::new(checkpoint_block_range.to) + .with_merkle_changesets_stage_checkpoint(MerkleChangeSetsCheckpoint { + block_range: checkpoint_block_range, + }); + + Ok(ExecOutput::done(checkpoint)) + } + + fn unwind( + &mut self, + provider: &Provider, + input: UnwindInput, + ) -> Result { + // Unwinding is trivial; just clear everything after the target block. + provider.clear_trie_changesets_from(input.unwind_to + 1)?; + + let mut computed_range = Self::computed_range(Some(input.checkpoint)); + computed_range.end = input.unwind_to + 1; + if computed_range.start > computed_range.end { + computed_range.start = computed_range.end; + } + + let checkpoint_block_range = CheckpointBlockRange { + from: computed_range.start, + // computed_range.end is exclusive + to: computed_range.end.saturating_sub(1), + }; + + let checkpoint = StageCheckpoint::new(input.unwind_to) + .with_merkle_changesets_stage_checkpoint(MerkleChangeSetsCheckpoint { + block_range: checkpoint_block_range, + }); + + Ok(UnwindOutput { checkpoint }) + } +} diff --git a/crates/stages/stages/src/stages/mod.rs b/crates/stages/stages/src/stages/mod.rs index 7e57009e808..40c4cb91368 100644 --- a/crates/stages/stages/src/stages/mod.rs +++ b/crates/stages/stages/src/stages/mod.rs @@ -16,6 +16,8 @@ mod index_account_history; mod index_storage_history; /// Stage for computing state root. mod merkle; +/// Stage for computing merkle changesets. +mod merkle_changesets; mod prune; /// The sender recovery stage. mod sender_recovery; @@ -32,6 +34,7 @@ pub use headers::*; pub use index_account_history::*; pub use index_storage_history::*; pub use merkle::*; +pub use merkle_changesets::*; pub use prune::*; pub use sender_recovery::*; pub use tx_lookup::*; @@ -223,7 +226,7 @@ mod tests { // In an unpruned configuration there is 1 receipt, 3 changed accounts and 1 changed // storage. - let mut prune = PruneModes::none(); + let mut prune = PruneModes::default(); check_pruning(test_db.factory.clone(), prune.clone(), 1, 3, 1).await; prune.receipts = Some(PruneMode::Full); diff --git a/crates/stages/stages/src/stages/prune.rs b/crates/stages/stages/src/stages/prune.rs index f62259dcfdd..3161d4b1412 100644 --- a/crates/stages/stages/src/stages/prune.rs +++ b/crates/stages/stages/src/stages/prune.rs @@ -1,7 +1,7 @@ use reth_db_api::{table::Value, transaction::DbTxMut}; use reth_primitives_traits::NodePrimitives; use reth_provider::{ - BlockReader, DBProvider, PruneCheckpointReader, PruneCheckpointWriter, + BlockReader, ChainStateBlockReader, DBProvider, PruneCheckpointReader, PruneCheckpointWriter, StaticFileProviderFactory, }; use reth_prune::{ @@ -42,6 +42,7 @@ where + PruneCheckpointReader + PruneCheckpointWriter + BlockReader + + ChainStateBlockReader + StaticFileProviderFactory< Primitives: NodePrimitives, >, @@ -121,7 +122,7 @@ impl PruneSenderRecoveryStage { /// Create new prune sender recovery stage with the given prune mode and commit threshold. pub fn new(prune_mode: PruneMode, commit_threshold: usize) -> Self { Self(PruneStage::new( - PruneModes { sender_recovery: Some(prune_mode), ..PruneModes::none() }, + PruneModes { sender_recovery: Some(prune_mode), ..PruneModes::default() }, commit_threshold, )) } @@ -133,6 +134,7 @@ where + PruneCheckpointReader + PruneCheckpointWriter + BlockReader + + ChainStateBlockReader + StaticFileProviderFactory< Primitives: NodePrimitives, >, diff --git a/crates/stages/types/src/checkpoints.rs b/crates/stages/types/src/checkpoints.rs index 61c399d9ac3..16bee1387f6 100644 --- a/crates/stages/types/src/checkpoints.rs +++ b/crates/stages/types/src/checkpoints.rs @@ -287,6 +287,17 @@ pub struct IndexHistoryCheckpoint { pub progress: EntitiesCheckpoint, } +/// Saves the progress of `MerkleChangeSets` stage. +#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] +#[cfg_attr(any(test, feature = "test-utils"), derive(arbitrary::Arbitrary))] +#[cfg_attr(any(test, feature = "reth-codec"), derive(reth_codecs::Compact))] +#[cfg_attr(any(test, feature = "reth-codec"), reth_codecs::add_arbitrary_tests(compact))] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct MerkleChangeSetsCheckpoint { + /// Block range which this checkpoint is valid for. + pub block_range: CheckpointBlockRange, +} + /// Saves the progress of abstract stage iterating over or downloading entities. #[derive(Debug, Default, PartialEq, Eq, Clone, Copy)] #[cfg_attr(any(test, feature = "test-utils"), derive(arbitrary::Arbitrary))] @@ -386,6 +397,9 @@ impl StageCheckpoint { StageId::IndexStorageHistory | StageId::IndexAccountHistory => { StageUnitCheckpoint::IndexHistory(IndexHistoryCheckpoint::default()) } + StageId::MerkleChangeSets => { + StageUnitCheckpoint::MerkleChangeSets(MerkleChangeSetsCheckpoint::default()) + } _ => return self, }); _ = self.stage_checkpoint.map(|mut checkpoint| checkpoint.set_block_range(from, to)); @@ -411,6 +425,7 @@ impl StageCheckpoint { progress: entities, .. }) => Some(entities), + StageUnitCheckpoint::MerkleChangeSets(_) => None, } } } @@ -436,6 +451,8 @@ pub enum StageUnitCheckpoint { Headers(HeadersCheckpoint), /// Saves the progress of Index History stage. IndexHistory(IndexHistoryCheckpoint), + /// Saves the progress of `MerkleChangeSets` stage. + MerkleChangeSets(MerkleChangeSetsCheckpoint), } impl StageUnitCheckpoint { @@ -446,7 +463,8 @@ impl StageUnitCheckpoint { Self::Account(AccountHashingCheckpoint { block_range, .. }) | Self::Storage(StorageHashingCheckpoint { block_range, .. }) | Self::Execution(ExecutionCheckpoint { block_range, .. }) | - Self::IndexHistory(IndexHistoryCheckpoint { block_range, .. }) => { + Self::IndexHistory(IndexHistoryCheckpoint { block_range, .. }) | + Self::MerkleChangeSets(MerkleChangeSetsCheckpoint { block_range, .. }) => { let old_range = *block_range; *block_range = CheckpointBlockRange { from, to }; @@ -544,6 +562,15 @@ stage_unit_checkpoints!( index_history_stage_checkpoint, /// Sets the stage checkpoint to index history. with_index_history_stage_checkpoint + ), + ( + 6, + MerkleChangeSets, + MerkleChangeSetsCheckpoint, + /// Returns the merkle changesets stage checkpoint, if any. + merkle_changesets_stage_checkpoint, + /// Sets the stage checkpoint to merkle changesets. + with_merkle_changesets_stage_checkpoint ) ); diff --git a/crates/stages/types/src/id.rs b/crates/stages/types/src/id.rs index 78d7e0ec1b6..8c0a91c8731 100644 --- a/crates/stages/types/src/id.rs +++ b/crates/stages/types/src/id.rs @@ -25,6 +25,7 @@ pub enum StageId { TransactionLookup, IndexStorageHistory, IndexAccountHistory, + MerkleChangeSets, Prune, Finish, /// Other custom stage with a provided string identifier. @@ -39,7 +40,7 @@ static ENCODED_STAGE_IDS: OnceLock>> = OnceLock::new(); impl StageId { /// All supported Stages - pub const ALL: [Self; 15] = [ + pub const ALL: [Self; 16] = [ Self::Era, Self::Headers, Self::Bodies, @@ -53,6 +54,7 @@ impl StageId { Self::TransactionLookup, Self::IndexStorageHistory, Self::IndexAccountHistory, + Self::MerkleChangeSets, Self::Prune, Self::Finish, ]; @@ -88,6 +90,7 @@ impl StageId { Self::TransactionLookup => "TransactionLookup", Self::IndexAccountHistory => "IndexAccountHistory", Self::IndexStorageHistory => "IndexStorageHistory", + Self::MerkleChangeSets => "MerkleChangeSets", Self::Prune => "Prune", Self::Finish => "Finish", Self::Other(s) => s, diff --git a/crates/stages/types/src/lib.rs b/crates/stages/types/src/lib.rs index 4e30ce27cd7..83585fee7ce 100644 --- a/crates/stages/types/src/lib.rs +++ b/crates/stages/types/src/lib.rs @@ -18,8 +18,8 @@ pub use id::StageId; mod checkpoints; pub use checkpoints::{ AccountHashingCheckpoint, CheckpointBlockRange, EntitiesCheckpoint, ExecutionCheckpoint, - HeadersCheckpoint, IndexHistoryCheckpoint, MerkleCheckpoint, StageCheckpoint, - StageUnitCheckpoint, StorageHashingCheckpoint, StorageRootMerkleCheckpoint, + HeadersCheckpoint, IndexHistoryCheckpoint, MerkleChangeSetsCheckpoint, MerkleCheckpoint, + StageCheckpoint, StageUnitCheckpoint, StorageHashingCheckpoint, StorageRootMerkleCheckpoint, }; mod execution; diff --git a/crates/stateless/Cargo.toml b/crates/stateless/Cargo.toml index 36a891ac3d2..8adbae28ae3 100644 --- a/crates/stateless/Cargo.toml +++ b/crates/stateless/Cargo.toml @@ -36,3 +36,11 @@ thiserror.workspace = true itertools.workspace = true serde.workspace = true serde_with.workspace = true + +k256 = { workspace = true, optional = true } +secp256k1 = { workspace = true, optional = true } + +[features] +default = ["k256"] +k256 = ["dep:k256"] +secp256k1 = ["dep:secp256k1"] diff --git a/crates/stateless/src/lib.rs b/crates/stateless/src/lib.rs index 1e858b9f9fb..6813638485e 100644 --- a/crates/stateless/src/lib.rs +++ b/crates/stateless/src/lib.rs @@ -35,9 +35,12 @@ extern crate alloc; +mod recover_block; /// Sparse trie implementation for stateless validation pub mod trie; +#[doc(inline)] +pub use recover_block::UncompressedPublicKey; #[doc(inline)] pub use trie::StatelessTrie; #[doc(inline)] diff --git a/crates/stateless/src/recover_block.rs b/crates/stateless/src/recover_block.rs new file mode 100644 index 00000000000..15db1fe55e1 --- /dev/null +++ b/crates/stateless/src/recover_block.rs @@ -0,0 +1,143 @@ +use crate::validation::StatelessValidationError; +use alloc::vec::Vec; +use alloy_consensus::BlockHeader; +use alloy_primitives::{Address, Signature, B256}; +use core::ops::Deref; +use reth_chainspec::EthereumHardforks; +use reth_ethereum_primitives::{Block, TransactionSigned}; +use reth_primitives_traits::{Block as _, RecoveredBlock}; +use serde::{Deserialize, Serialize}; +use serde_with::{serde_as, Bytes}; + +#[cfg(all(feature = "k256", feature = "secp256k1"))] +use k256 as _; + +/// Serialized uncompressed public key +#[serde_as] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct UncompressedPublicKey(#[serde_as(as = "Bytes")] pub [u8; 65]); + +impl Deref for UncompressedPublicKey { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +/// Verifies all transactions in a block against a list of public keys and signatures. +/// +/// Returns a `RecoveredBlock` +pub(crate) fn recover_block_with_public_keys( + block: Block, + public_keys: Vec, + chain_spec: &ChainSpec, +) -> Result, StatelessValidationError> +where + ChainSpec: EthereumHardforks, +{ + if block.body().transactions.len() != public_keys.len() { + return Err(StatelessValidationError::Custom( + "Number of public keys must match number of transactions", + )); + } + + // Determine if we're in the Homestead fork for signature validation + let is_homestead = chain_spec.is_homestead_active_at_block(block.header().number()); + + // Verify each transaction signature against its corresponding public key + let senders = public_keys + .iter() + .zip(block.body().transactions()) + .map(|(vk, tx)| verify_and_compute_sender(vk, tx, is_homestead)) + .collect::, _>>()?; + + // Create RecoveredBlock with verified senders + let block_hash = block.hash_slow(); + Ok(RecoveredBlock::new(block, senders, block_hash)) +} + +/// Verifies a transaction using its signature and the given public key. +/// +/// Note: If the signature or the public key is incorrect, then this method +/// will return an error. +/// +/// Returns the address derived from the public key. +fn verify_and_compute_sender( + vk: &UncompressedPublicKey, + tx: &TransactionSigned, + is_homestead: bool, +) -> Result { + let sig = tx.signature(); + + // non-normalized signatures are only valid pre-homestead + let sig_is_normalized = sig.normalize_s().is_none(); + if is_homestead && !sig_is_normalized { + return Err(StatelessValidationError::HomesteadSignatureNotNormalized); + } + let sig_hash = tx.signature_hash(); + #[cfg(all(feature = "k256", feature = "secp256k1"))] + { + let _ = verify_and_compute_sender_unchecked_k256; + } + #[cfg(feature = "secp256k1")] + { + verify_and_compute_sender_unchecked_secp256k1(vk, sig, sig_hash) + } + #[cfg(all(feature = "k256", not(feature = "secp256k1")))] + { + verify_and_compute_sender_unchecked_k256(vk, sig, sig_hash) + } + #[cfg(not(any(feature = "secp256k1", feature = "k256")))] + { + let _ = vk; + let _ = tx; + let _: B256 = sig_hash; + let _: &Signature = sig; + + unimplemented!("Must choose either k256 or secp256k1 feature") + } +} +#[cfg(feature = "k256")] +fn verify_and_compute_sender_unchecked_k256( + vk: &UncompressedPublicKey, + sig: &Signature, + sig_hash: B256, +) -> Result { + use k256::ecdsa::{signature::hazmat::PrehashVerifier, VerifyingKey}; + + let vk = + VerifyingKey::from_sec1_bytes(vk).map_err(|_| StatelessValidationError::SignerRecovery)?; + + sig.to_k256() + .and_then(|sig| vk.verify_prehash(sig_hash.as_slice(), &sig)) + .map_err(|_| StatelessValidationError::SignerRecovery)?; + + Ok(Address::from_public_key(&vk)) +} + +#[cfg(feature = "secp256k1")] +fn verify_and_compute_sender_unchecked_secp256k1( + vk: &UncompressedPublicKey, + sig: &Signature, + sig_hash: B256, +) -> Result { + use secp256k1::{ecdsa::Signature as SecpSignature, Message, PublicKey, SECP256K1}; + + let public_key = + PublicKey::from_slice(vk).map_err(|_| StatelessValidationError::SignerRecovery)?; + + let mut sig_bytes = [0u8; 64]; + sig_bytes[0..32].copy_from_slice(&sig.r().to_be_bytes::<32>()); + sig_bytes[32..64].copy_from_slice(&sig.s().to_be_bytes::<32>()); + + let signature = SecpSignature::from_compact(&sig_bytes) + .map_err(|_| StatelessValidationError::SignerRecovery)?; + + let message = Message::from_digest(sig_hash.0); + SECP256K1 + .verify_ecdsa(&message, &signature, &public_key) + .map_err(|_| StatelessValidationError::SignerRecovery)?; + + Ok(Address::from_raw_public_key(&vk[1..])) +} diff --git a/crates/stateless/src/validation.rs b/crates/stateless/src/validation.rs index 38b96d6bd0f..a0475b09939 100644 --- a/crates/stateless/src/validation.rs +++ b/crates/stateless/src/validation.rs @@ -1,4 +1,5 @@ use crate::{ + recover_block::{recover_block_with_public_keys, UncompressedPublicKey}, trie::{StatelessSparseTrie, StatelessTrie}, witness_db::WitnessDatabase, ExecutionWitness, @@ -89,6 +90,14 @@ pub enum StatelessValidationError { expected: B256, }, + /// Error during signer recovery. + #[error("signer recovery failed")] + SignerRecovery, + + /// Error when signature has non-normalized s value in homestead block. + #[error("signature s value not normalized for homestead block")] + HomesteadSignatureNotNormalized, + /// Custom error. #[error("{0}")] Custom(&'static str), @@ -130,7 +139,8 @@ pub enum StatelessValidationError { /// If all steps succeed the function returns `Some` containing the hash of the validated /// `current_block`. pub fn stateless_validation( - current_block: RecoveredBlock, + current_block: Block, + public_keys: Vec, witness: ExecutionWitness, chain_spec: Arc, evm_config: E, @@ -141,6 +151,7 @@ where { stateless_validation_with_trie::( current_block, + public_keys, witness, chain_spec, evm_config, @@ -154,7 +165,8 @@ where /// /// See `stateless_validation` for detailed documentation of the validation process. pub fn stateless_validation_with_trie( - current_block: RecoveredBlock, + current_block: Block, + public_keys: Vec, witness: ExecutionWitness, chain_spec: Arc, evm_config: E, @@ -164,6 +176,8 @@ where ChainSpec: Send + Sync + EthChainSpec
+ EthereumHardforks + Debug, E: ConfigureEvm + Clone + 'static, { + let current_block = recover_block_with_public_keys(current_block, public_keys, &*chain_spec)?; + let mut ancestor_headers: Vec<_> = witness .headers .iter() diff --git a/crates/storage/codecs/src/lib.rs b/crates/storage/codecs/src/lib.rs index 67e5f32b07c..1ac37966c2e 100644 --- a/crates/storage/codecs/src/lib.rs +++ b/crates/storage/codecs/src/lib.rs @@ -312,10 +312,9 @@ where return (None, buf) } - let (len, mut buf) = decode_varuint(buf); + let (len, buf) = decode_varuint(buf); - let (element, _) = T::from_compact(&buf[..len], len); - buf.advance(len); + let (element, buf) = T::from_compact(buf, len); (Some(element), buf) } diff --git a/crates/storage/db-api/src/cursor.rs b/crates/storage/db-api/src/cursor.rs index 3aeee949ea1..068b64a3c97 100644 --- a/crates/storage/db-api/src/cursor.rs +++ b/crates/storage/db-api/src/cursor.rs @@ -87,7 +87,7 @@ pub trait DbDupCursorRO { /// | `key` | `subkey` | **Equivalent starting position** | /// |--------|----------|-----------------------------------------| /// | `None` | `None` | [`DbCursorRO::first()`] | - /// | `Some` | `None` | [`DbCursorRO::seek()`] | + /// | `Some` | `None` | [`DbCursorRO::seek_exact()`] | /// | `None` | `Some` | [`DbDupCursorRO::seek_by_key_subkey()`] | /// | `Some` | `Some` | [`DbDupCursorRO::seek_by_key_subkey()`] | fn walk_dup( diff --git a/crates/storage/db-api/src/models/accounts.rs b/crates/storage/db-api/src/models/accounts.rs index 263e362cc6a..41a11e1c7e5 100644 --- a/crates/storage/db-api/src/models/accounts.rs +++ b/crates/storage/db-api/src/models/accounts.rs @@ -176,7 +176,11 @@ impl Decode for AddressStorageKey { } } -impl_fixed_arbitrary!((BlockNumberAddress, 28), (AddressStorageKey, 52)); +impl_fixed_arbitrary!( + (BlockNumberAddress, 28), + (BlockNumberHashedAddress, 40), + (AddressStorageKey, 52) +); #[cfg(test)] mod tests { @@ -209,6 +213,31 @@ mod tests { assert_eq!(bytes, Encode::encode(key)); } + #[test] + fn test_block_number_hashed_address() { + let num = 1u64; + let hash = B256::from_slice(&[0xba; 32]); + let key = BlockNumberHashedAddress((num, hash)); + + let mut bytes = [0u8; 40]; + bytes[..8].copy_from_slice(&num.to_be_bytes()); + bytes[8..].copy_from_slice(hash.as_slice()); + + let encoded = Encode::encode(key); + assert_eq!(encoded, bytes); + + let decoded: BlockNumberHashedAddress = Decode::decode(&encoded).unwrap(); + assert_eq!(decoded, key); + } + + #[test] + fn test_block_number_hashed_address_rand() { + let mut bytes = [0u8; 40]; + rng().fill(bytes.as_mut_slice()); + let key = BlockNumberHashedAddress::arbitrary(&mut Unstructured::new(&bytes)).unwrap(); + assert_eq!(bytes, Encode::encode(key)); + } + #[test] fn test_address_storage_key() { let storage_key = StorageKey::random(); diff --git a/crates/storage/db-api/src/models/mod.rs b/crates/storage/db-api/src/models/mod.rs index 24951789f5d..31d9b301f8c 100644 --- a/crates/storage/db-api/src/models/mod.rs +++ b/crates/storage/db-api/src/models/mod.rs @@ -12,7 +12,9 @@ use reth_ethereum_primitives::{Receipt, TransactionSigned, TxType}; use reth_primitives_traits::{Account, Bytecode, StorageEntry}; use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::StageCheckpoint; -use reth_trie_common::{StoredNibbles, StoredNibblesSubKey, *}; +use reth_trie_common::{ + StorageTrieEntry, StoredNibbles, StoredNibblesSubKey, TrieChangeSetsEntry, *, +}; use serde::{Deserialize, Serialize}; pub mod accounts; @@ -219,6 +221,7 @@ impl_compression_for_compact!( TxType, StorageEntry, BranchNodeCompact, + TrieChangeSetsEntry, StoredNibbles, StoredNibblesSubKey, StorageTrieEntry, diff --git a/crates/storage/db-api/src/tables/mod.rs b/crates/storage/db-api/src/tables/mod.rs index 259b2d39b15..cd678260128 100644 --- a/crates/storage/db-api/src/tables/mod.rs +++ b/crates/storage/db-api/src/tables/mod.rs @@ -21,8 +21,8 @@ use crate::{ accounts::BlockNumberAddress, blocks::{HeaderHash, StoredBlockOmmers}, storage_sharded_key::StorageShardedKey, - AccountBeforeTx, ClientVersion, CompactU256, IntegerList, ShardedKey, - StoredBlockBodyIndices, StoredBlockWithdrawals, + AccountBeforeTx, BlockNumberHashedAddress, ClientVersion, CompactU256, IntegerList, + ShardedKey, StoredBlockBodyIndices, StoredBlockWithdrawals, }, table::{Decode, DupSort, Encode, Table, TableInfo}, }; @@ -32,7 +32,9 @@ use reth_ethereum_primitives::{Receipt, TransactionSigned}; use reth_primitives_traits::{Account, Bytecode, StorageEntry}; use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::StageCheckpoint; -use reth_trie_common::{BranchNodeCompact, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey}; +use reth_trie_common::{ + BranchNodeCompact, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey, TrieChangeSetsEntry, +}; use serde::{Deserialize, Serialize}; use std::fmt; @@ -486,6 +488,20 @@ tables! { type SubKey = StoredNibblesSubKey; } + /// Stores the state of a node in the accounts trie prior to a particular block being executed. + table AccountsTrieChangeSets { + type Key = BlockNumber; + type Value = TrieChangeSetsEntry; + type SubKey = StoredNibblesSubKey; + } + + /// Stores the state of a node in a storage trie prior to a particular block being executed. + table StoragesTrieChangeSets { + type Key = BlockNumberHashedAddress; + type Value = TrieChangeSetsEntry; + type SubKey = StoredNibblesSubKey; + } + /// Stores the transaction sender for each canonical transaction. /// It is needed to speed up execution stage and allows fetching signer without doing /// transaction signed recovery diff --git a/crates/storage/db-common/src/init.rs b/crates/storage/db-common/src/init.rs index 48442aab381..8b24f0f8d19 100644 --- a/crates/storage/db-common/src/init.rs +++ b/crates/storage/db-common/src/init.rs @@ -9,7 +9,9 @@ use reth_config::config::EtlConfig; use reth_db_api::{tables, transaction::DbTxMut, DatabaseError}; use reth_etl::Collector; use reth_execution_errors::StateRootError; -use reth_primitives_traits::{Account, Bytecode, GotExpected, NodePrimitives, StorageEntry}; +use reth_primitives_traits::{ + Account, Bytecode, GotExpected, NodePrimitives, SealedHeader, StorageEntry, +}; use reth_provider::{ errors::provider::ProviderResult, providers::StaticFileWriter, BlockHashReader, BlockNumReader, BundleStateInit, ChainSpecProvider, DBProvider, DatabaseProviderFactory, ExecutionOutcome, @@ -389,13 +391,16 @@ where } let block = provider_rw.last_block_number()?; + let hash = provider_rw .block_hash(block)? .ok_or_else(|| eyre::eyre!("Block hash not found for block {}", block))?; - let expected_state_root = provider_rw + let header = provider_rw .header_by_number(block)? - .ok_or_else(|| ProviderError::HeaderNotFound(block.into()))? - .state_root(); + .map(SealedHeader::seal_slow) + .ok_or_else(|| ProviderError::HeaderNotFound(block.into()))?; + + let expected_state_root = header.state_root(); // first line can be state root let dump_state_root = parse_state_root(&mut reader)?; @@ -403,6 +408,7 @@ where error!(target: "reth::cli", ?dump_state_root, ?expected_state_root, + header=?header.num_hash(), "State root from state dump does not match state root in current header." ); return Err(InitStorageError::StateRootMismatch(GotExpected { @@ -602,7 +608,7 @@ where match state_root.root_with_progress()? { StateRootProgress::Progress(state, _, updates) => { - let updated_len = provider.write_trie_updates(&updates)?; + let updated_len = provider.write_trie_updates(updates)?; total_flushed_updates += updated_len; trace!(target: "reth::cli", @@ -622,7 +628,7 @@ where } } StateRootProgress::Complete(root, _, updates) => { - let updated_len = provider.write_trie_updates(&updates)?; + let updated_len = provider.write_trie_updates(updates)?; total_flushed_updates += updated_len; trace!(target: "reth::cli", diff --git a/crates/storage/errors/src/provider.rs b/crates/storage/errors/src/provider.rs index c27587690ba..47cc630bcb6 100644 --- a/crates/storage/errors/src/provider.rs +++ b/crates/storage/errors/src/provider.rs @@ -137,6 +137,14 @@ pub enum ProviderError { /// Missing trie updates. #[error("missing trie updates for block {0}")] MissingTrieUpdates(B256), + /// Insufficient changesets to revert to the requested block. + #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")] + InsufficientChangesets { + /// The block number requested for reversion + requested: BlockNumber, + /// The available range of blocks with changesets + available: core::ops::RangeInclusive, + }, /// Any other error type wrapped into a cloneable [`AnyError`]. #[error(transparent)] Other(#[from] AnyError), diff --git a/crates/storage/provider/src/bundle_state/mod.rs b/crates/storage/provider/src/bundle_state/mod.rs deleted file mode 100644 index 58b76f1eacf..00000000000 --- a/crates/storage/provider/src/bundle_state/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -//! Bundle state module. -//! This module contains all the logic related to bundle state. - -mod state_reverts; -pub use state_reverts::StorageRevertsIter; diff --git a/crates/storage/provider/src/changesets_utils/mod.rs b/crates/storage/provider/src/changesets_utils/mod.rs new file mode 100644 index 00000000000..3b65825264b --- /dev/null +++ b/crates/storage/provider/src/changesets_utils/mod.rs @@ -0,0 +1,7 @@ +//! This module contains helpful utilities related to populating changesets tables. + +mod state_reverts; +pub use state_reverts::StorageRevertsIter; + +mod trie; +pub use trie::*; diff --git a/crates/storage/provider/src/bundle_state/state_reverts.rs b/crates/storage/provider/src/changesets_utils/state_reverts.rs similarity index 100% rename from crates/storage/provider/src/bundle_state/state_reverts.rs rename to crates/storage/provider/src/changesets_utils/state_reverts.rs diff --git a/crates/storage/provider/src/changesets_utils/trie.rs b/crates/storage/provider/src/changesets_utils/trie.rs new file mode 100644 index 00000000000..f4365aab103 --- /dev/null +++ b/crates/storage/provider/src/changesets_utils/trie.rs @@ -0,0 +1,147 @@ +use itertools::{merge_join_by, EitherOrBoth}; +use reth_db_api::DatabaseError; +use reth_trie::{trie_cursor::TrieCursor, BranchNodeCompact, Nibbles}; +use std::cmp::{Ord, Ordering}; + +/// Combines a sorted iterator of trie node paths and a storage trie cursor into a new +/// iterator which produces the current values of all given paths in the same order. +#[derive(Debug)] +pub struct StorageTrieCurrentValuesIter<'cursor, P, C> { + /// Sorted iterator of node paths which we want the values of. + paths: P, + /// Storage trie cursor. + cursor: &'cursor mut C, + /// Current value at the cursor, allows us to treat the cursor as a peekable iterator. + cursor_current: Option<(Nibbles, BranchNodeCompact)>, +} + +impl<'cursor, P, C> StorageTrieCurrentValuesIter<'cursor, P, C> +where + P: Iterator, + C: TrieCursor, +{ + /// Instantiate a [`StorageTrieCurrentValuesIter`] from a sorted paths iterator and a cursor. + pub fn new(paths: P, cursor: &'cursor mut C) -> Result { + let mut new_self = Self { paths, cursor, cursor_current: None }; + new_self.seek_cursor(Nibbles::default())?; + Ok(new_self) + } + + fn seek_cursor(&mut self, path: Nibbles) -> Result<(), DatabaseError> { + self.cursor_current = self.cursor.seek(path)?; + Ok(()) + } +} + +impl<'cursor, P, C> Iterator for StorageTrieCurrentValuesIter<'cursor, P, C> +where + P: Iterator, + C: TrieCursor, +{ + type Item = Result<(Nibbles, Option), DatabaseError>; + + fn next(&mut self) -> Option { + let Some(curr_path) = self.paths.next() else { + // If there are no more paths then there is no further possible output. + return None + }; + + // If the path is ahead of the cursor then seek the cursor forward to catch up. The cursor + // will seek either to `curr_path` or beyond it. + if self.cursor_current.as_ref().is_some_and(|(cursor_path, _)| curr_path > *cursor_path) && + let Err(err) = self.seek_cursor(curr_path) + { + return Some(Err(err)) + } + + // If there is a path but the cursor is empty then that path has no node. + if self.cursor_current.is_none() { + return Some(Ok((curr_path, None))) + } + + let (cursor_path, cursor_node) = + self.cursor_current.as_mut().expect("already checked for None"); + + // There is both a path and a cursor value, compare their paths. + match curr_path.cmp(cursor_path) { + Ordering::Less => { + // If the path is behind the cursor then there is no value for that + // path, produce None. + Some(Ok((curr_path, None))) + } + Ordering::Equal => { + // If the target path and cursor's path match then there is a value for that path, + // return the value. We don't seek the cursor here, that will be handled on the + // next call to `next` after checking that `paths` isn't None. + let cursor_node = core::mem::take(cursor_node); + Some(Ok((*cursor_path, Some(cursor_node)))) + } + Ordering::Greater => { + panic!("cursor was seeked to {curr_path:?}, but produced a node at a lower path {cursor_path:?}") + } + } + } +} + +/// Returns an iterator which produces the values to be inserted into the `StoragesTrieChangeSets` +/// table for an account whose storage was wiped during a block. It is expected that this is called +/// prior to inserting the block's trie updates. +/// +/// ## Arguments +/// +/// - `curr_values_of_changed` is an iterator over the current values of all trie nodes modified by +/// the block, ordered by path. +/// - `all_nodes` is an iterator over all existing trie nodes for the account, ordered by path. +/// +/// ## Returns +/// +/// An iterator of trie node paths and a `Some(node)` (indicating the node was wiped) or a `None` +/// (indicating the node was modified in the block but didn't previously exist. The iterator's +/// results will be ordered by path. +pub fn storage_trie_wiped_changeset_iter( + curr_values_of_changed: impl Iterator< + Item = Result<(Nibbles, Option), DatabaseError>, + >, + all_nodes: impl Iterator>, +) -> Result< + impl Iterator), DatabaseError>>, + DatabaseError, +> { + let all_nodes = all_nodes.map(|e| e.map(|(nibbles, node)| (nibbles, Some(node)))); + + let merged = merge_join_by(curr_values_of_changed, all_nodes, |a, b| match (a, b) { + (Err(_), _) => Ordering::Less, + (_, Err(_)) => Ordering::Greater, + (Ok(a), Ok(b)) => a.0.cmp(&b.0), + }); + + Ok(merged.map(|either_or| match either_or { + EitherOrBoth::Left(changed) => { + // A path of a changed node (given in `paths`) which was not found in the database (or + // there's an error). The current value of this path must be None, otherwise it would + // have also been returned by the `all_nodes` iter. + debug_assert!( + changed.as_ref().is_err() || changed.as_ref().is_ok_and(|(_, node)| node.is_none()), + "changed node is Some but wasn't returned by `all_nodes` iterator: {changed:?}", + ); + changed + } + EitherOrBoth::Right(wiped) => { + // A node was found in the db (indicating it was wiped) but was not given in `paths`. + // Return it as-is. + wiped + } + EitherOrBoth::Both(changed, _wiped) => { + // A path of a changed node (given in `paths`) was found with a previous value in the + // database. The changed node must have a value which is equal to the one found by the + // `all_nodes` iterator. If the changed node had no previous value (None) it wouldn't + // be returned by `all_nodes` and so would be in the Left branch. + // + // Due to the ordering closure passed to `merge_join_by` it's not possible for either + // value to be an error here. + debug_assert!(changed.is_ok(), "unreachable error condition: {changed:?}"); + debug_assert_eq!(changed, _wiped); + changed + } + })) +} diff --git a/crates/storage/provider/src/lib.rs b/crates/storage/provider/src/lib.rs index c281f117908..70822c604bb 100644 --- a/crates/storage/provider/src/lib.rs +++ b/crates/storage/provider/src/lib.rs @@ -35,7 +35,7 @@ pub use static_file::StaticFileSegment; pub use reth_execution_types::*; -pub mod bundle_state; +pub mod changesets_utils; /// Re-export `OriginalValuesKnown` pub use revm_database::states::OriginalValuesKnown; diff --git a/crates/storage/provider/src/providers/blockchain_provider.rs b/crates/storage/provider/src/providers/blockchain_provider.rs index 7040032eca0..512b8569de2 100644 --- a/crates/storage/provider/src/providers/blockchain_provider.rs +++ b/crates/storage/provider/src/providers/blockchain_provider.rs @@ -6,7 +6,7 @@ use crate::{ HashedPostStateProvider, HeaderProvider, ProviderError, ProviderFactory, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StateProviderBox, StateProviderFactory, StateReader, StaticFileProviderFactory, TransactionVariant, - TransactionsProvider, + TransactionsProvider, TrieReader, }; use alloy_consensus::transaction::TransactionMeta; use alloy_eips::{BlockHashOrNumber, BlockId, BlockNumHash, BlockNumberOrTag}; @@ -25,7 +25,7 @@ use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; use reth_storage_api::{BlockBodyIndicesProvider, NodePrimitivesProvider, StorageChangeSetReader}; use reth_storage_errors::provider::ProviderResult; -use reth_trie::{HashedPostState, KeccakKeyHasher}; +use reth_trie::{updates::TrieUpdatesSorted, HashedPostState, KeccakKeyHasher}; use revm_database::BundleState; use std::{ ops::{RangeBounds, RangeInclusive}, @@ -739,6 +739,19 @@ impl StateReader for BlockchainProvider { } } +impl TrieReader for BlockchainProvider { + fn trie_reverts(&self, from: BlockNumber) -> ProviderResult { + self.consistent_provider()?.trie_reverts(from) + } + + fn get_block_trie_updates( + &self, + block_number: BlockNumber, + ) -> ProviderResult { + self.consistent_provider()?.get_block_trie_updates(block_number) + } +} + #[cfg(test)] mod tests { use crate::{ @@ -755,8 +768,7 @@ mod tests { use rand::Rng; use reth_chain_state::{ test_utils::TestBlockBuilder, CanonStateNotification, CanonStateSubscriptions, - CanonicalInMemoryState, ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates, - NewCanonicalChain, + CanonicalInMemoryState, ExecutedBlock, NewCanonicalChain, }; use reth_chainspec::{ChainSpec, MAINNET}; use reth_db_api::models::{AccountBeforeTx, StoredBlockBodyIndices}; @@ -882,12 +894,14 @@ mod tests { let execution_outcome = ExecutionOutcome { receipts: vec![block_receipts], ..Default::default() }; - ExecutedBlockWithTrieUpdates::new( - Arc::new(RecoveredBlock::new_sealed(block.clone(), senders)), - execution_outcome.into(), - Default::default(), - ExecutedTrieUpdates::empty(), - ) + ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + block.clone(), + senders, + )), + execution_output: execution_outcome.into(), + ..Default::default() + } }) .collect(), }; @@ -1009,15 +1023,13 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlockWithTrieUpdates::new( - Arc::new(RecoveredBlock::new_sealed( + new: vec![ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, )), - Default::default(), - Default::default(), - ExecutedTrieUpdates::empty(), - )], + ..Default::default() + }], }; provider.canonical_in_memory_state.update_chain(chain); @@ -1045,16 +1057,12 @@ mod tests { assert_eq!(provider.find_block_by_hash(first_db_block.hash(), BlockSource::Pending)?, None); // Insert the last block into the pending state - provider.canonical_in_memory_state.set_pending_block(ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - last_in_mem_block.clone(), - Default::default(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), - }, - trie: ExecutedTrieUpdates::empty(), + provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + last_in_mem_block.clone(), + Default::default(), + )), + ..Default::default() }); // Now the last block should be found in memory @@ -1105,15 +1113,13 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlockWithTrieUpdates::new( - Arc::new(RecoveredBlock::new_sealed( + new: vec![ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, )), - Default::default(), - Default::default(), - ExecutedTrieUpdates::empty(), - )], + ..Default::default() + }], }; provider.canonical_in_memory_state.update_chain(chain); @@ -1159,16 +1165,12 @@ mod tests { ); // Set the block as pending - provider.canonical_in_memory_state.set_pending_block(ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - block.clone(), - block.senders().unwrap(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), - }, - trie: ExecutedTrieUpdates::empty(), + provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + block.clone(), + block.senders().unwrap(), + )), + ..Default::default() }); // Assertions related to the pending block @@ -1206,15 +1208,13 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlockWithTrieUpdates::new( - Arc::new(RecoveredBlock::new_sealed( + new: vec![ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, )), - Default::default(), - Default::default(), - ExecutedTrieUpdates::empty(), - )], + ..Default::default() + }], }; provider.canonical_in_memory_state.update_chain(chain); @@ -1686,9 +1686,12 @@ mod tests { .first() .map(|block| { let senders = block.senders().expect("failed to recover senders"); - ExecutedBlockWithTrieUpdates::new( - Arc::new(RecoveredBlock::new_sealed(block.clone(), senders)), - Arc::new(ExecutionOutcome { + ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + block.clone(), + senders, + )), + execution_output: Arc::new(ExecutionOutcome { bundle: BundleState::new( in_memory_state.into_iter().map(|(address, (account, _))| { (address, None, Some(account.into()), Default::default()) @@ -1701,9 +1704,8 @@ mod tests { first_block: first_in_memory_block, ..Default::default() }), - Default::default(), - ExecutedTrieUpdates::empty(), - ) + ..Default::default() + } }) .unwrap()], }; @@ -1821,19 +1823,13 @@ mod tests { // adding a pending block to state can test pending() and pending_state_by_hash() function let pending_block = database_blocks[database_blocks.len() - 1].clone(); - only_database_provider.canonical_in_memory_state.set_pending_block( - ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - pending_block.clone(), - Default::default(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), - }, - trie: ExecutedTrieUpdates::empty(), - }, - ); + only_database_provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + pending_block.clone(), + Default::default(), + )), + ..Default::default() + }); assert_eq!( pending_block.hash(), @@ -1919,16 +1915,12 @@ mod tests { // Set the pending block in memory let pending_block = in_memory_blocks.last().unwrap(); - provider.canonical_in_memory_state.set_pending_block(ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - pending_block.clone(), - Default::default(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), - }, - trie: ExecutedTrieUpdates::empty(), + provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + pending_block.clone(), + Default::default(), + )), + ..Default::default() }); // Set the safe block in memory diff --git a/crates/storage/provider/src/providers/consistent.rs b/crates/storage/provider/src/providers/consistent.rs index 93415e8e347..66a35e5e9b1 100644 --- a/crates/storage/provider/src/providers/consistent.rs +++ b/crates/storage/provider/src/providers/consistent.rs @@ -4,7 +4,7 @@ use crate::{ BlockReader, BlockReaderIdExt, BlockSource, ChainSpecProvider, ChangeSetReader, HeaderProvider, ProviderError, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StateReader, StaticFileProviderFactory, TransactionVariant, - TransactionsProvider, + TransactionsProvider, TrieReader, }; use alloy_consensus::{transaction::TransactionMeta, BlockHeader}; use alloy_eips::{ @@ -28,6 +28,7 @@ use reth_storage_api::{ StorageChangeSetReader, TryIntoHistoricalStateProvider, }; use reth_storage_errors::provider::ProviderResult; +use reth_trie::updates::TrieUpdatesSorted; use revm_database::states::PlainStorageRevert; use std::{ ops::{Add, Bound, RangeBounds, RangeInclusive, Sub}, @@ -1504,6 +1505,19 @@ impl StateReader for ConsistentProvider { } } +impl TrieReader for ConsistentProvider { + fn trie_reverts(&self, from: BlockNumber) -> ProviderResult { + self.storage_provider.trie_reverts(from) + } + + fn get_block_trie_updates( + &self, + block_number: BlockNumber, + ) -> ProviderResult { + self.storage_provider.get_block_trie_updates(block_number) + } +} + #[cfg(test)] mod tests { use crate::{ @@ -1514,9 +1528,7 @@ mod tests { use alloy_primitives::B256; use itertools::Itertools; use rand::Rng; - use reth_chain_state::{ - ExecutedBlock, ExecutedBlockWithTrieUpdates, ExecutedTrieUpdates, NewCanonicalChain, - }; + use reth_chain_state::{ExecutedBlock, NewCanonicalChain}; use reth_db_api::models::AccountBeforeTx; use reth_ethereum_primitives::Block; use reth_execution_types::ExecutionOutcome; @@ -1619,15 +1631,13 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlockWithTrieUpdates::new( - Arc::new(RecoveredBlock::new_sealed( + new: vec![ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, )), - Default::default(), - Default::default(), - ExecutedTrieUpdates::empty(), - )], + ..Default::default() + }], }; consistent_provider.canonical_in_memory_state.update_chain(chain); let consistent_provider = provider.consistent_provider()?; @@ -1661,16 +1671,12 @@ mod tests { ); // Insert the last block into the pending state - provider.canonical_in_memory_state.set_pending_block(ExecutedBlockWithTrieUpdates { - block: ExecutedBlock { - recovered_block: Arc::new(RecoveredBlock::new_sealed( - last_in_mem_block.clone(), - Default::default(), - )), - execution_output: Default::default(), - hashed_state: Default::default(), - }, - trie: ExecutedTrieUpdates::empty(), + provider.canonical_in_memory_state.set_pending_block(ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + last_in_mem_block.clone(), + Default::default(), + )), + ..Default::default() }); // Now the last block should be found in memory @@ -1729,15 +1735,13 @@ mod tests { let in_memory_block_senders = first_in_mem_block.senders().expect("failed to recover senders"); let chain = NewCanonicalChain::Commit { - new: vec![ExecutedBlockWithTrieUpdates::new( - Arc::new(RecoveredBlock::new_sealed( + new: vec![ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( first_in_mem_block.clone(), in_memory_block_senders, )), - Default::default(), - Default::default(), - ExecutedTrieUpdates::empty(), - )], + ..Default::default() + }], }; consistent_provider.canonical_in_memory_state.update_chain(chain); @@ -1834,9 +1838,12 @@ mod tests { .first() .map(|block| { let senders = block.senders().expect("failed to recover senders"); - ExecutedBlockWithTrieUpdates::new( - Arc::new(RecoveredBlock::new_sealed(block.clone(), senders)), - Arc::new(ExecutionOutcome { + ExecutedBlock { + recovered_block: Arc::new(RecoveredBlock::new_sealed( + block.clone(), + senders, + )), + execution_output: Arc::new(ExecutionOutcome { bundle: BundleState::new( in_memory_state.into_iter().map(|(address, (account, _))| { (address, None, Some(account.into()), Default::default()) @@ -1849,9 +1856,8 @@ mod tests { first_block: first_in_memory_block, ..Default::default() }), - Default::default(), - ExecutedTrieUpdates::empty(), - ) + ..Default::default() + } }) .unwrap()], }; diff --git a/crates/storage/provider/src/providers/database/mod.rs b/crates/storage/provider/src/providers/database/mod.rs index f7b3c4ba603..bd6b1e0f472 100644 --- a/crates/storage/provider/src/providers/database/mod.rs +++ b/crates/storage/provider/src/providers/database/mod.rs @@ -84,7 +84,7 @@ impl ProviderFactory { db, chain_spec, static_file_provider, - prune_modes: PruneModes::none(), + prune_modes: PruneModes::default(), storage: Default::default(), } } @@ -131,7 +131,7 @@ impl>> ProviderFactory { db: Arc::new(init_db(path, args).map_err(RethError::msg)?), chain_spec, static_file_provider, - prune_modes: PruneModes::none(), + prune_modes: PruneModes::default(), storage: Default::default(), }) } @@ -670,7 +670,7 @@ mod tests { let prune_modes = PruneModes { sender_recovery: Some(PruneMode::Full), transaction_lookup: Some(PruneMode::Full), - ..PruneModes::none() + ..PruneModes::default() }; let factory = create_test_provider_factory(); let provider = factory.with_prune_modes(prune_modes).provider_rw().unwrap(); diff --git a/crates/storage/provider/src/providers/database/provider.rs b/crates/storage/provider/src/providers/database/provider.rs index 6fdc37c4f53..235bf57a4a4 100644 --- a/crates/storage/provider/src/providers/database/provider.rs +++ b/crates/storage/provider/src/providers/database/provider.rs @@ -1,5 +1,7 @@ use crate::{ - bundle_state::StorageRevertsIter, + changesets_utils::{ + storage_trie_wiped_changeset_iter, StorageRevertsIter, StorageTrieCurrentValuesIter, + }, providers::{ database::{chain::ChainStorage, metrics}, static_file::StaticFileWriter, @@ -16,7 +18,7 @@ use crate::{ OriginalValuesKnown, ProviderError, PruneCheckpointReader, PruneCheckpointWriter, RevertsInit, StageCheckpointReader, StateProviderBox, StateWriter, StaticFileProviderFactory, StatsReader, StorageReader, StorageTrieWriter, TransactionVariant, TransactionsProvider, - TransactionsProviderExt, TrieWriter, + TransactionsProviderExt, TrieReader, TrieWriter, }; use alloy_consensus::{ transaction::{SignerRecoverable, TransactionMeta, TxHashRef}, @@ -30,14 +32,14 @@ use alloy_primitives::{ }; use itertools::Itertools; use rayon::slice::ParallelSliceMut; -use reth_chain_state::{ExecutedBlock, ExecutedBlockWithTrieUpdates}; +use reth_chain_state::ExecutedBlock; use reth_chainspec::{ChainInfo, ChainSpecProvider, EthChainSpec, EthereumHardforks}; use reth_db_api::{ cursor::{DbCursorRO, DbCursorRW, DbDupCursorRO, DbDupCursorRW}, database::Database, models::{ sharded_key, storage_sharded_key::StorageShardedKey, AccountBeforeTx, BlockNumberAddress, - ShardedKey, StoredBlockBodyIndices, + BlockNumberHashedAddress, ShardedKey, StoredBlockBodyIndices, }, table::Table, tables, @@ -47,8 +49,7 @@ use reth_db_api::{ use reth_execution_types::{Chain, ExecutionOutcome}; use reth_node_types::{BlockTy, BodyTy, HeaderTy, NodeTypes, ReceiptTy, TxTy}; use reth_primitives_traits::{ - Account, Block as _, BlockBody as _, Bytecode, GotExpected, RecoveredBlock, SealedHeader, - StorageEntry, + Account, Block as _, BlockBody as _, Bytecode, RecoveredBlock, SealedHeader, StorageEntry, }; use reth_prune_types::{ PruneCheckpoint, PruneMode, PruneModes, PruneSegment, MINIMUM_PRUNING_DISTANCE, @@ -59,13 +60,19 @@ use reth_storage_api::{ BlockBodyIndicesProvider, BlockBodyReader, NodePrimitivesProvider, StateProvider, StorageChangeSetReader, TryIntoHistoricalStateProvider, }; -use reth_storage_errors::provider::{ProviderResult, RootMismatch}; +use reth_storage_errors::provider::ProviderResult; use reth_trie::{ - prefix_set::{PrefixSet, PrefixSetMut, TriePrefixSets}, - updates::{StorageTrieUpdates, TrieUpdates}, - HashedPostStateSorted, Nibbles, StateRoot, StoredNibbles, + trie_cursor::{ + InMemoryTrieCursor, InMemoryTrieCursorFactory, TrieCursor, TrieCursorFactory, + TrieCursorIter, + }, + updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted}, + BranchNodeCompact, HashedPostStateSorted, Nibbles, StoredNibbles, StoredNibblesSubKey, + TrieChangeSetsEntry, +}; +use reth_trie_db::{ + DatabaseAccountTrieCursor, DatabaseStorageTrieCursor, DatabaseTrieCursorFactory, }; -use reth_trie_db::{DatabaseStateRoot, DatabaseStorageTrieCursor}; use revm_database::states::{ PlainStateReverts, PlainStorageChangeset, PlainStorageRevert, StateChangeset, }; @@ -73,7 +80,7 @@ use std::{ cmp::Ordering, collections::{BTreeMap, BTreeSet}, fmt::Debug, - ops::{Deref, DerefMut, Not, Range, RangeBounds, RangeInclusive}, + ops::{Deref, DerefMut, Not, Range, RangeBounds, RangeFrom, RangeInclusive}, sync::Arc, }; use tracing::{debug, trace}; @@ -254,10 +261,7 @@ impl AsRef for DatabaseProvider { impl DatabaseProvider { /// Writes executed blocks and state to storage. - pub fn save_blocks( - &self, - blocks: Vec>, - ) -> ProviderResult<()> { + pub fn save_blocks(&self, blocks: Vec>) -> ProviderResult<()> { if blocks.is_empty() { debug!(target: "providers::db", "Attempted to write empty block range"); return Ok(()) @@ -281,12 +285,10 @@ impl DatabaseProvider DatabaseProvider DatabaseProvider, - ) -> ProviderResult<()> { + pub fn unwind_trie_state_from(&self, from: BlockNumber) -> ProviderResult<()> { let changed_accounts = self .tx .cursor_read::()? - .walk_range(range.clone())? + .walk_range(from..)? .collect::, _>>()?; - // Unwind account hashes. Add changed accounts to account prefix set. - let hashed_addresses = self.unwind_account_hashing(changed_accounts.iter())?; - let mut account_prefix_set = PrefixSetMut::with_capacity(hashed_addresses.len()); - let mut destroyed_accounts = HashSet::default(); - for (hashed_address, account) in hashed_addresses { - account_prefix_set.insert(Nibbles::unpack(hashed_address)); - if account.is_none() { - destroyed_accounts.insert(hashed_address); - } - } + // Unwind account hashes. + self.unwind_account_hashing(changed_accounts.iter())?; // Unwind account history indices. self.unwind_account_history_indices(changed_accounts.iter())?; - let storage_range = BlockNumberAddress::range(range.clone()); + let storage_start = BlockNumberAddress((from, Address::ZERO)); let changed_storages = self .tx .cursor_read::()? - .walk_range(storage_range)? + .walk_range(storage_start..)? .collect::, _>>()?; - // Unwind storage hashes. Add changed account and storage keys to corresponding prefix - // sets. - let mut storage_prefix_sets = B256Map::::default(); - let storage_entries = self.unwind_storage_hashing(changed_storages.iter().copied())?; - for (hashed_address, hashed_slots) in storage_entries { - account_prefix_set.insert(Nibbles::unpack(hashed_address)); - let mut storage_prefix_set = PrefixSetMut::with_capacity(hashed_slots.len()); - for slot in hashed_slots { - storage_prefix_set.insert(Nibbles::unpack(slot)); - } - storage_prefix_sets.insert(hashed_address, storage_prefix_set.freeze()); - } + // Unwind storage hashes. + self.unwind_storage_hashing(changed_storages.iter().copied())?; // Unwind storage history indices. self.unwind_storage_history_indices(changed_storages.iter().copied())?; - // Calculate the reverted merkle root. - // This is the same as `StateRoot::incremental_root_with_updates`, only the prefix sets - // are pre-loaded. - let prefix_sets = TriePrefixSets { - account_prefix_set: account_prefix_set.freeze(), - storage_prefix_sets, - destroyed_accounts, - }; - let (new_state_root, trie_updates) = StateRoot::from_tx(&self.tx) - .with_prefix_sets(prefix_sets) - .root_with_updates() - .map_err(reth_db_api::DatabaseError::from)?; - - let parent_number = range.start().saturating_sub(1); - let parent_state_root = self - .header_by_number(parent_number)? - .ok_or_else(|| ProviderError::HeaderNotFound(parent_number.into()))? - .state_root(); - - // state root should be always correct as we are reverting state. - // but for sake of double verification we will check it again. - if new_state_root != parent_state_root { - let parent_hash = self - .block_hash(parent_number)? - .ok_or_else(|| ProviderError::HeaderNotFound(parent_number.into()))?; - return Err(ProviderError::UnwindStateRootMismatch(Box::new(RootMismatch { - root: GotExpected { got: new_state_root, expected: parent_state_root }, - block_number: parent_number, - block_hash: parent_hash, - }))) - } - self.write_trie_updates(&trie_updates)?; + // Unwind accounts/storages trie tables using the revert. + let trie_revert = self.trie_reverts(from)?; + self.write_trie_updates_sorted(&trie_revert)?; + + // Clear trie changesets which have been unwound. + self.clear_trie_changesets_from(from)?; Ok(()) } @@ -1773,6 +1730,10 @@ impl StateWriter // If we are writing the primary storage wipe transition, the pre-existing plain // storage state has to be taken from the database and written to storage history. // See [StorageWipe::Primary] for more details. + // + // TODO(mediocregopher): This could be rewritten in a way which doesn't require + // collecting wiped entries into a Vec like this, see + // `write_storage_trie_changesets`. let mut wiped_storage = Vec::new(); if wiped { tracing::trace!(?address, "Wiping storage"); @@ -2143,8 +2104,10 @@ impl StateWriter } impl TrieWriter for DatabaseProvider { - /// Writes trie updates. Returns the number of entries modified. - fn write_trie_updates(&self, trie_updates: &TrieUpdates) -> ProviderResult { + /// Writes trie updates to the database with already sorted updates. + /// + /// Returns the number of entries modified. + fn write_trie_updates_sorted(&self, trie_updates: &TrieUpdatesSorted) -> ProviderResult { if trie_updates.is_empty() { return Ok(0) } @@ -2152,23 +2115,11 @@ impl TrieWriter for DatabaseProvider // Track the number of inserted entries. let mut num_entries = 0; - // Merge updated and removed nodes. Updated nodes must take precedence. - let mut account_updates = trie_updates - .removed_nodes_ref() - .iter() - .filter_map(|n| { - (!trie_updates.account_nodes_ref().contains_key(n)).then_some((n, None)) - }) - .collect::>(); - account_updates.extend( - trie_updates.account_nodes_ref().iter().map(|(nibbles, node)| (nibbles, Some(node))), - ); - // Sort trie node updates. - account_updates.sort_unstable_by(|a, b| a.0.cmp(b.0)); - let tx = self.tx_ref(); let mut account_trie_cursor = tx.cursor_write::()?; - for (key, updated_node) in account_updates { + + // Process sorted account nodes + for (key, updated_node) in &trie_updates.account_nodes { let nibbles = StoredNibbles(*key); match updated_node { Some(node) => { @@ -2186,18 +2137,226 @@ impl TrieWriter for DatabaseProvider } } - num_entries += self.write_storage_trie_updates(trie_updates.storage_tries_ref().iter())?; + num_entries += + self.write_storage_trie_updates_sorted(trie_updates.storage_tries_ref().iter())?; + + Ok(num_entries) + } + + /// Records the current values of all trie nodes which will be updated using the `TrieUpdates` + /// into the trie changesets tables. + /// + /// The intended usage of this method is to call it _prior_ to calling `write_trie_updates` with + /// the same `TrieUpdates`. + /// + /// Returns the number of keys written. + fn write_trie_changesets( + &self, + block_number: BlockNumber, + trie_updates: &TrieUpdatesSorted, + updates_overlay: Option<&TrieUpdatesSorted>, + ) -> ProviderResult { + let mut num_entries = 0; + + let mut changeset_cursor = + self.tx_ref().cursor_dup_write::()?; + let curr_values_cursor = self.tx_ref().cursor_read::()?; + + // Wrap the cursor in DatabaseAccountTrieCursor + let mut db_account_cursor = DatabaseAccountTrieCursor::new(curr_values_cursor); + + // Static empty array for when updates_overlay is None + static EMPTY_ACCOUNT_UPDATES: Vec<(Nibbles, Option)> = Vec::new(); + + // Get the overlay updates for account trie, or use an empty array + let account_overlay_updates = updates_overlay + .map(|overlay| overlay.account_nodes_ref()) + .unwrap_or(&EMPTY_ACCOUNT_UPDATES); + + // Wrap the cursor in InMemoryTrieCursor with the overlay + let mut in_memory_account_cursor = + InMemoryTrieCursor::new(Some(&mut db_account_cursor), account_overlay_updates); + + for (path, _) in trie_updates.account_nodes_ref() { + num_entries += 1; + let node = in_memory_account_cursor.seek_exact(*path)?.map(|(_, node)| node); + changeset_cursor.append_dup( + block_number, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(*path), node }, + )?; + } + + let mut storage_updates = trie_updates.storage_tries.iter().collect::>(); + storage_updates.sort_unstable_by(|a, b| a.0.cmp(b.0)); + + num_entries += self.write_storage_trie_changesets( + block_number, + storage_updates.into_iter(), + updates_overlay, + )?; Ok(num_entries) } + + fn clear_trie_changesets(&self) -> ProviderResult<()> { + let tx = self.tx_ref(); + tx.clear::()?; + tx.clear::()?; + Ok(()) + } + + fn clear_trie_changesets_from(&self, from: BlockNumber) -> ProviderResult<()> { + let tx = self.tx_ref(); + { + let range = from..; + let mut cursor = tx.cursor_dup_write::()?; + let mut walker = cursor.walk_range(range)?; + + while walker.next().transpose()?.is_some() { + walker.delete_current()?; + } + } + + { + let range: RangeFrom = (from, B256::ZERO).into()..; + let mut cursor = tx.cursor_dup_write::()?; + let mut walker = cursor.walk_range(range)?; + + while walker.next().transpose()?.is_some() { + walker.delete_current()?; + } + } + + Ok(()) + } +} + +impl TrieReader for DatabaseProvider { + fn trie_reverts(&self, from: BlockNumber) -> ProviderResult { + let tx = self.tx_ref(); + + // Read account trie changes directly into a Vec - data is already sorted by nibbles + // within each block, and we want the oldest (first) version of each node + let mut account_nodes = Vec::new(); + let mut seen_account_keys = HashSet::new(); + let mut accounts_cursor = tx.cursor_dup_read::()?; + + for entry in accounts_cursor.walk_range(from..)? { + let (_, TrieChangeSetsEntry { nibbles, node }) = entry?; + // Only keep the first (oldest) version of each node + if seen_account_keys.insert(nibbles.0) { + account_nodes.push((nibbles.0, node)); + } + } + + // Read storage trie changes - data is sorted by (block, hashed_address, nibbles) + // Keep track of seen (address, nibbles) pairs to only keep the oldest version + let mut storage_tries = B256Map::>::default(); + let mut seen_storage_keys = HashSet::new(); + let mut storages_cursor = tx.cursor_dup_read::()?; + + // Create storage range starting from `from` block + let storage_range_start = BlockNumberHashedAddress((from, B256::ZERO)); + + for entry in storages_cursor.walk_range(storage_range_start..)? { + let ( + BlockNumberHashedAddress((_, hashed_address)), + TrieChangeSetsEntry { nibbles, node }, + ) = entry?; + + // Only keep the first (oldest) version of each node for this address + if seen_storage_keys.insert((hashed_address, nibbles.0)) { + storage_tries.entry(hashed_address).or_default().push((nibbles.0, node)); + } + } + + // Convert to StorageTrieUpdatesSorted + let storage_tries = storage_tries + .into_iter() + .map(|(address, nodes)| { + (address, StorageTrieUpdatesSorted { storage_nodes: nodes, is_deleted: false }) + }) + .collect(); + + Ok(TrieUpdatesSorted { account_nodes, storage_tries }) + } + + fn get_block_trie_updates( + &self, + block_number: BlockNumber, + ) -> ProviderResult { + let tx = self.tx_ref(); + + // Step 1: Get the trie reverts for the state after the target block + let reverts = self.trie_reverts(block_number + 1)?; + + // Step 2: Create an InMemoryTrieCursorFactory with the reverts + // This gives us the trie state as it was after the target block was processed + let db_cursor_factory = DatabaseTrieCursorFactory::new(tx); + let cursor_factory = InMemoryTrieCursorFactory::new(db_cursor_factory, &reverts); + + // Step 3: Collect all account trie nodes that changed in the target block + let mut trie_updates = TrieUpdatesSorted::default(); + + // Walk through all account trie changes for this block + let mut accounts_trie_cursor = tx.cursor_dup_read::()?; + let mut account_cursor = cursor_factory.account_trie_cursor()?; + + for entry in accounts_trie_cursor.walk_dup(Some(block_number), None)? { + let (_, TrieChangeSetsEntry { nibbles, .. }) = entry?; + // Look up the current value of this trie node using the overlay cursor + let node_value = account_cursor.seek_exact(nibbles.0)?.map(|(_, node)| node); + trie_updates.account_nodes.push((nibbles.0, node_value)); + } + + // Step 4: Collect all storage trie nodes that changed in the target block + let mut storages_trie_cursor = tx.cursor_dup_read::()?; + let storage_range_start = BlockNumberHashedAddress((block_number, B256::ZERO)); + let storage_range_end = BlockNumberHashedAddress((block_number + 1, B256::ZERO)); + + let mut current_hashed_address = None; + let mut storage_cursor = None; + + for entry in storages_trie_cursor.walk_range(storage_range_start..storage_range_end)? { + let ( + BlockNumberHashedAddress((_, hashed_address)), + TrieChangeSetsEntry { nibbles, .. }, + ) = entry?; + + // Check if we need to create a new storage cursor for a different account + if current_hashed_address != Some(hashed_address) { + storage_cursor = Some(cursor_factory.storage_trie_cursor(hashed_address)?); + current_hashed_address = Some(hashed_address); + } + + // Look up the current value of this storage trie node + let cursor = + storage_cursor.as_mut().expect("storage_cursor was just initialized above"); + let node_value = cursor.seek_exact(nibbles.0)?.map(|(_, node)| node); + trie_updates + .storage_tries + .entry(hashed_address) + .or_insert_with(|| StorageTrieUpdatesSorted { + storage_nodes: Vec::new(), + is_deleted: false, + }) + .storage_nodes + .push((nibbles.0, node_value)); + } + + Ok(trie_updates) + } } impl StorageTrieWriter for DatabaseProvider { - /// Writes storage trie updates from the given storage trie map. First sorts the storage trie - /// updates by the hashed address, writing in sorted order. - fn write_storage_trie_updates<'a>( + /// Writes storage trie updates from the given storage trie map with already sorted updates. + /// + /// Expects the storage trie updates to already be sorted by the hashed address key. + /// + /// Returns the number of entries modified. + fn write_storage_trie_updates_sorted<'a>( &self, - storage_tries: impl Iterator, + storage_tries: impl Iterator, ) -> ProviderResult { let mut num_entries = 0; let mut storage_tries = storage_tries.collect::>(); @@ -2207,12 +2366,110 @@ impl StorageTrieWriter for DatabaseP let mut db_storage_trie_cursor = DatabaseStorageTrieCursor::new(cursor, *hashed_address); num_entries += - db_storage_trie_cursor.write_storage_trie_updates(storage_trie_updates)?; + db_storage_trie_cursor.write_storage_trie_updates_sorted(storage_trie_updates)?; cursor = db_storage_trie_cursor.cursor; } Ok(num_entries) } + + /// Records the current values of all trie nodes which will be updated using the + /// `StorageTrieUpdates` into the storage trie changesets table. + /// + /// The intended usage of this method is to call it _prior_ to calling + /// `write_storage_trie_updates` with the same set of `StorageTrieUpdates`. + /// + /// Returns the number of keys written. + fn write_storage_trie_changesets<'a>( + &self, + block_number: BlockNumber, + storage_tries: impl Iterator, + updates_overlay: Option<&TrieUpdatesSorted>, + ) -> ProviderResult { + let mut num_written = 0; + + let mut changeset_cursor = + self.tx_ref().cursor_dup_write::()?; + + // We hold two cursors to the same table because we use them simultaneously when an + // account's storage is wiped. We keep them outside the for-loop so they can be re-used + // between accounts. + let changed_curr_values_cursor = self.tx_ref().cursor_dup_read::()?; + let wiped_nodes_cursor = self.tx_ref().cursor_dup_read::()?; + + // DatabaseStorageTrieCursor requires ownership of the cursor. The easiest way to deal with + // this is to create this outer variable with an initial dummy account, and overwrite it on + // every loop for every real account. + let mut changed_curr_values_cursor = DatabaseStorageTrieCursor::new( + changed_curr_values_cursor, + B256::default(), // Will be set per iteration + ); + let mut wiped_nodes_cursor = DatabaseStorageTrieCursor::new( + wiped_nodes_cursor, + B256::default(), // Will be set per iteration + ); + + // Static empty array for when updates_overlay is None + static EMPTY_UPDATES: Vec<(Nibbles, Option)> = Vec::new(); + + for (hashed_address, storage_trie_updates) in storage_tries { + let changeset_key = BlockNumberHashedAddress((block_number, *hashed_address)); + + // Update the hashed address for the cursors + changed_curr_values_cursor = + DatabaseStorageTrieCursor::new(changed_curr_values_cursor.cursor, *hashed_address); + + // Get the overlay updates for this storage trie, or use an empty array + let overlay_updates = updates_overlay + .and_then(|overlay| overlay.storage_tries.get(hashed_address)) + .map(|updates| updates.storage_nodes_ref()) + .unwrap_or(&EMPTY_UPDATES); + + // Wrap the cursor in InMemoryTrieCursor with the overlay + let mut in_memory_changed_cursor = + InMemoryTrieCursor::new(Some(&mut changed_curr_values_cursor), overlay_updates); + + // Create an iterator which produces the current values of all updated paths, or None if + // they are currently unset. + let curr_values_of_changed = StorageTrieCurrentValuesIter::new( + storage_trie_updates.storage_nodes.iter().map(|e| e.0), + &mut in_memory_changed_cursor, + )?; + + if storage_trie_updates.is_deleted() { + // Create an iterator that starts from the beginning of the storage trie for this + // account + wiped_nodes_cursor = + DatabaseStorageTrieCursor::new(wiped_nodes_cursor.cursor, *hashed_address); + + // Wrap the wiped nodes cursor in InMemoryTrieCursor with the overlay + let mut in_memory_wiped_cursor = + InMemoryTrieCursor::new(Some(&mut wiped_nodes_cursor), overlay_updates); + + let all_nodes = TrieCursorIter::new(&mut in_memory_wiped_cursor); + + for wiped in storage_trie_wiped_changeset_iter(curr_values_of_changed, all_nodes)? { + let (path, node) = wiped?; + num_written += 1; + changeset_cursor.append_dup( + changeset_key, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(path), node }, + )?; + } + } else { + for curr_value in curr_values_of_changed { + let (path, node) = curr_value?; + num_written += 1; + changeset_cursor.append_dup( + changeset_key, + TrieChangeSetsEntry { nibbles: StoredNibblesSubKey(path), node }, + )?; + } + } + } + + Ok(num_written) + } } impl HashingWriter for DatabaseProvider { @@ -2507,7 +2764,7 @@ impl BlockExecu ) -> ProviderResult> { let range = block + 1..=self.last_block_number()?; - self.unwind_trie_state_range(range.clone())?; + self.unwind_trie_state_from(block + 1)?; // get execution res let execution_state = self.take_state_above(block)?; @@ -2525,9 +2782,7 @@ impl BlockExecu } fn remove_block_and_execution_above(&self, block: BlockNumber) -> ProviderResult<()> { - let range = block + 1..=self.last_block_number()?; - - self.unwind_trie_state_range(range)?; + self.unwind_trie_state_from(block + 1)?; // remove execution res self.remove_state_above(block)?; @@ -3139,4 +3394,1275 @@ mod tests { assert_eq!(range_result, individual_results); } + + #[test] + fn test_write_trie_changesets() { + use reth_db_api::models::BlockNumberHashedAddress; + use reth_trie::{BranchNodeCompact, StorageTrieEntry}; + + let factory = create_test_provider_factory(); + let provider_rw = factory.provider_rw().unwrap(); + + let block_number = 1u64; + + // Create some test nibbles and nodes + let account_nibbles1 = Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]); + let account_nibbles2 = Nibbles::from_nibbles([0x5, 0x6, 0x7, 0x8]); + + let node1 = BranchNodeCompact::new( + 0b1111_1111_1111_1111, // state_mask + 0b0000_0000_0000_0000, // tree_mask + 0b0000_0000_0000_0000, // hash_mask + vec![], // hashes + None, // root hash + ); + + // Pre-populate AccountsTrie with a node that will be updated (for account_nibbles1) + { + let mut cursor = provider_rw.tx_ref().cursor_write::().unwrap(); + cursor.insert(StoredNibbles(account_nibbles1), &node1).unwrap(); + } + + // Create account trie updates: one Some (update) and one None (removal) + let account_nodes = vec![ + (account_nibbles1, Some(node1.clone())), // This will update existing node + (account_nibbles2, None), // This will be a removal (no existing node) + ]; + + // Create storage trie updates + let storage_address1 = B256::from([1u8; 32]); // Normal storage trie + let storage_address2 = B256::from([2u8; 32]); // Wiped storage trie + + let storage_nibbles1 = Nibbles::from_nibbles([0xa, 0xb]); + let storage_nibbles2 = Nibbles::from_nibbles([0xc, 0xd]); + let storage_nibbles3 = Nibbles::from_nibbles([0xe, 0xf]); + + let storage_node1 = BranchNodeCompact::new( + 0b1111_0000_0000_0000, + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + let storage_node2 = BranchNodeCompact::new( + 0b0000_1111_0000_0000, + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + // Create an old version of storage_node1 to prepopulate + let storage_node1_old = BranchNodeCompact::new( + 0b1010_0000_0000_0000, // Different mask to show it's an old value + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + // Pre-populate StoragesTrie for normal storage (storage_address1) + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_write::().unwrap(); + // Add node that will be updated (storage_nibbles1) with old value + let entry = StorageTrieEntry { + nibbles: StoredNibblesSubKey(storage_nibbles1), + node: storage_node1_old.clone(), + }; + cursor.upsert(storage_address1, &entry).unwrap(); + } + + // Pre-populate StoragesTrie for wiped storage (storage_address2) + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_write::().unwrap(); + // Add node that will be updated (storage_nibbles1) + let entry1 = StorageTrieEntry { + nibbles: StoredNibblesSubKey(storage_nibbles1), + node: storage_node1.clone(), + }; + cursor.upsert(storage_address2, &entry1).unwrap(); + // Add node that won't be updated but exists (storage_nibbles3) + let entry3 = StorageTrieEntry { + nibbles: StoredNibblesSubKey(storage_nibbles3), + node: storage_node2.clone(), + }; + cursor.upsert(storage_address2, &entry3).unwrap(); + } + + // Normal storage trie: one Some (update) and one None (new) + let storage_trie1 = StorageTrieUpdatesSorted { + is_deleted: false, + storage_nodes: vec![ + (storage_nibbles1, Some(storage_node1.clone())), // This will update existing node + (storage_nibbles2, None), // This is a new node + ], + }; + + // Wiped storage trie + let storage_trie2 = StorageTrieUpdatesSorted { + is_deleted: true, + storage_nodes: vec![ + (storage_nibbles1, Some(storage_node1.clone())), // Updated node already in db + (storage_nibbles2, Some(storage_node2.clone())), /* Updated node not in db + * storage_nibbles3 is in db + * but not updated */ + ], + }; + + let mut storage_tries = B256Map::default(); + storage_tries.insert(storage_address1, storage_trie1); + storage_tries.insert(storage_address2, storage_trie2); + + let trie_updates = TrieUpdatesSorted { account_nodes, storage_tries }; + + // Write the changesets + let num_written = + provider_rw.write_trie_changesets(block_number, &trie_updates, None).unwrap(); + + // Verify number of entries written + // Account changesets: 2 (one update, one removal) + // Storage changesets: + // - Normal storage: 2 (one update, one removal) + // - Wiped storage: 3 (two updated, one existing not updated) + // Total: 2 + 2 + 3 = 7 + assert_eq!(num_written, 7); + + // Verify account changesets were written correctly + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_read::().unwrap(); + + // Get all entries for this block to see what was written + let all_entries = cursor + .walk_dup(Some(block_number), None) + .unwrap() + .collect::, _>>() + .unwrap(); + + // Assert the full value of all_entries in a single assert_eq + assert_eq!( + all_entries, + vec![ + ( + block_number, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(account_nibbles1), + node: Some(node1), + } + ), + ( + block_number, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(account_nibbles2), + node: None, + } + ), + ] + ); + } + + // Verify storage changesets were written correctly + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_read::().unwrap(); + + // Check normal storage trie changesets + let key1 = BlockNumberHashedAddress((block_number, storage_address1)); + let entries1 = + cursor.walk_dup(Some(key1), None).unwrap().collect::, _>>().unwrap(); + + assert_eq!( + entries1, + vec![ + ( + key1, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles1), + node: Some(storage_node1_old), // Old value that was prepopulated + } + ), + ( + key1, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles2), + node: None, // New node, no previous value + } + ), + ] + ); + + // Check wiped storage trie changesets + let key2 = BlockNumberHashedAddress((block_number, storage_address2)); + let entries2 = + cursor.walk_dup(Some(key2), None).unwrap().collect::, _>>().unwrap(); + + assert_eq!( + entries2, + vec![ + ( + key2, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles1), + node: Some(storage_node1), // Was in db, so has old value + } + ), + ( + key2, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles2), + node: None, // Was not in db + } + ), + ( + key2, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles3), + node: Some(storage_node2), // Existing node in wiped storage + } + ), + ] + ); + } + + provider_rw.commit().unwrap(); + } + + #[test] + fn test_write_trie_changesets_with_overlay() { + use reth_db_api::models::BlockNumberHashedAddress; + use reth_trie::BranchNodeCompact; + + let factory = create_test_provider_factory(); + let provider_rw = factory.provider_rw().unwrap(); + + let block_number = 1u64; + + // Create some test nibbles and nodes + let account_nibbles1 = Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]); + let account_nibbles2 = Nibbles::from_nibbles([0x5, 0x6, 0x7, 0x8]); + + let node1 = BranchNodeCompact::new( + 0b1111_1111_1111_1111, // state_mask + 0b0000_0000_0000_0000, // tree_mask + 0b0000_0000_0000_0000, // hash_mask + vec![], // hashes + None, // root hash + ); + + // NOTE: Unlike the previous test, we're NOT pre-populating the database + // All node values will come from the overlay + + // Create the overlay with existing values that would normally be in the DB + let node1_old = BranchNodeCompact::new( + 0b1010_1010_1010_1010, // Different mask to show it's the overlay "existing" value + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + // Create overlay account nodes + let overlay_account_nodes = vec![ + (account_nibbles1, Some(node1_old.clone())), // This simulates existing node in overlay + ]; + + // Create account trie updates: one Some (update) and one None (removal) + let account_nodes = vec![ + (account_nibbles1, Some(node1)), // This will update overlay node + (account_nibbles2, None), // This will be a removal (no existing node) + ]; + + // Create storage trie updates + let storage_address1 = B256::from([1u8; 32]); // Normal storage trie + let storage_address2 = B256::from([2u8; 32]); // Wiped storage trie + + let storage_nibbles1 = Nibbles::from_nibbles([0xa, 0xb]); + let storage_nibbles2 = Nibbles::from_nibbles([0xc, 0xd]); + let storage_nibbles3 = Nibbles::from_nibbles([0xe, 0xf]); + + let storage_node1 = BranchNodeCompact::new( + 0b1111_0000_0000_0000, + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + let storage_node2 = BranchNodeCompact::new( + 0b0000_1111_0000_0000, + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + // Create old versions for overlay + let storage_node1_old = BranchNodeCompact::new( + 0b1010_0000_0000_0000, // Different mask to show it's an old value + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + // Create overlay storage nodes + let mut overlay_storage_tries = B256Map::default(); + + // Overlay for normal storage (storage_address1) + let overlay_storage_trie1 = StorageTrieUpdatesSorted { + is_deleted: false, + storage_nodes: vec![ + (storage_nibbles1, Some(storage_node1_old.clone())), /* Simulates existing in + * overlay */ + ], + }; + + // Overlay for wiped storage (storage_address2) + let overlay_storage_trie2 = StorageTrieUpdatesSorted { + is_deleted: false, + storage_nodes: vec![ + (storage_nibbles1, Some(storage_node1.clone())), // Existing in overlay + (storage_nibbles3, Some(storage_node2.clone())), // Also existing in overlay + ], + }; + + overlay_storage_tries.insert(storage_address1, overlay_storage_trie1); + overlay_storage_tries.insert(storage_address2, overlay_storage_trie2); + + let overlay = TrieUpdatesSorted { + account_nodes: overlay_account_nodes, + storage_tries: overlay_storage_tries, + }; + + // Normal storage trie: one Some (update) and one None (new) + let storage_trie1 = StorageTrieUpdatesSorted { + is_deleted: false, + storage_nodes: vec![ + (storage_nibbles1, Some(storage_node1.clone())), // This will update overlay node + (storage_nibbles2, None), // This is a new node + ], + }; + + // Wiped storage trie + let storage_trie2 = StorageTrieUpdatesSorted { + is_deleted: true, + storage_nodes: vec![ + (storage_nibbles1, Some(storage_node1.clone())), // Updated node from overlay + (storage_nibbles2, Some(storage_node2.clone())), /* Updated node not in overlay + * storage_nibbles3 is in + * overlay + * but not updated */ + ], + }; + + let mut storage_tries = B256Map::default(); + storage_tries.insert(storage_address1, storage_trie1); + storage_tries.insert(storage_address2, storage_trie2); + + let trie_updates = TrieUpdatesSorted { account_nodes, storage_tries }; + + // Write the changesets WITH OVERLAY + let num_written = + provider_rw.write_trie_changesets(block_number, &trie_updates, Some(&overlay)).unwrap(); + + // Verify number of entries written + // Account changesets: 2 (one update from overlay, one removal) + // Storage changesets: + // - Normal storage: 2 (one update from overlay, one new) + // - Wiped storage: 3 (two updated, one existing from overlay not updated) + // Total: 2 + 2 + 3 = 7 + assert_eq!(num_written, 7); + + // Verify account changesets were written correctly + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_read::().unwrap(); + + // Get all entries for this block to see what was written + let all_entries = cursor + .walk_dup(Some(block_number), None) + .unwrap() + .collect::, _>>() + .unwrap(); + + // Assert the full value of all_entries in a single assert_eq + assert_eq!( + all_entries, + vec![ + ( + block_number, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(account_nibbles1), + node: Some(node1_old), // Value from overlay, not DB + } + ), + ( + block_number, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(account_nibbles2), + node: None, + } + ), + ] + ); + } + + // Verify storage changesets were written correctly + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_read::().unwrap(); + + // Check normal storage trie changesets + let key1 = BlockNumberHashedAddress((block_number, storage_address1)); + let entries1 = + cursor.walk_dup(Some(key1), None).unwrap().collect::, _>>().unwrap(); + + assert_eq!( + entries1, + vec![ + ( + key1, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles1), + node: Some(storage_node1_old), // Old value from overlay + } + ), + ( + key1, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles2), + node: None, // New node, no previous value + } + ), + ] + ); + + // Check wiped storage trie changesets + let key2 = BlockNumberHashedAddress((block_number, storage_address2)); + let entries2 = + cursor.walk_dup(Some(key2), None).unwrap().collect::, _>>().unwrap(); + + assert_eq!( + entries2, + vec![ + ( + key2, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles1), + node: Some(storage_node1), // Value from overlay + } + ), + ( + key2, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles2), + node: None, // Was not in overlay + } + ), + ( + key2, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles3), + node: Some(storage_node2), /* Existing node from overlay in wiped + * storage */ + } + ), + ] + ); + } + + provider_rw.commit().unwrap(); + } + + #[test] + fn test_clear_trie_changesets_from() { + use alloy_primitives::hex_literal::hex; + use reth_db_api::models::BlockNumberHashedAddress; + use reth_trie::{BranchNodeCompact, StoredNibblesSubKey, TrieChangeSetsEntry}; + + let factory = create_test_provider_factory(); + + // Create some test data for different block numbers + let block1 = 100u64; + let block2 = 101u64; + let block3 = 102u64; + let block4 = 103u64; + let block5 = 104u64; + + // Create test addresses for storage changesets + let storage_address1 = + B256::from(hex!("1111111111111111111111111111111111111111111111111111111111111111")); + let storage_address2 = + B256::from(hex!("2222222222222222222222222222222222222222222222222222222222222222")); + + // Create test nibbles + let nibbles1 = StoredNibblesSubKey(Nibbles::from_nibbles([0x1, 0x2, 0x3])); + let nibbles2 = StoredNibblesSubKey(Nibbles::from_nibbles([0x4, 0x5, 0x6])); + let nibbles3 = StoredNibblesSubKey(Nibbles::from_nibbles([0x7, 0x8, 0x9])); + + // Create test nodes + let node1 = BranchNodeCompact::new( + 0b1111_1111_1111_1111, + 0b1111_1111_1111_1111, + 0b0000_0000_0000_0001, + vec![B256::from(hex!( + "1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" + ))], + None, + ); + let node2 = BranchNodeCompact::new( + 0b1111_1111_1111_1110, + 0b1111_1111_1111_1110, + 0b0000_0000_0000_0010, + vec![B256::from(hex!( + "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890" + ))], + Some(B256::from(hex!( + "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef" + ))), + ); + + // Populate AccountsTrieChangeSets with data across multiple blocks + { + let provider_rw = factory.provider_rw().unwrap(); + let mut cursor = + provider_rw.tx_ref().cursor_dup_write::().unwrap(); + + // Block 100: 2 entries (will be kept - before start block) + cursor + .upsert( + block1, + &TrieChangeSetsEntry { nibbles: nibbles1.clone(), node: Some(node1.clone()) }, + ) + .unwrap(); + cursor + .upsert(block1, &TrieChangeSetsEntry { nibbles: nibbles2.clone(), node: None }) + .unwrap(); + + // Block 101: 3 entries with duplicates (will be deleted - from this block onwards) + cursor + .upsert( + block2, + &TrieChangeSetsEntry { nibbles: nibbles1.clone(), node: Some(node2.clone()) }, + ) + .unwrap(); + cursor + .upsert( + block2, + &TrieChangeSetsEntry { nibbles: nibbles1.clone(), node: Some(node1.clone()) }, + ) + .unwrap(); // duplicate key + cursor + .upsert(block2, &TrieChangeSetsEntry { nibbles: nibbles3.clone(), node: None }) + .unwrap(); + + // Block 102: 2 entries (will be deleted - after start block) + cursor + .upsert( + block3, + &TrieChangeSetsEntry { nibbles: nibbles2.clone(), node: Some(node1.clone()) }, + ) + .unwrap(); + cursor + .upsert( + block3, + &TrieChangeSetsEntry { nibbles: nibbles3.clone(), node: Some(node2.clone()) }, + ) + .unwrap(); + + // Block 103: 1 entry (will be deleted - after start block) + cursor + .upsert(block4, &TrieChangeSetsEntry { nibbles: nibbles1.clone(), node: None }) + .unwrap(); + + // Block 104: 2 entries (will be deleted - after start block) + cursor + .upsert( + block5, + &TrieChangeSetsEntry { nibbles: nibbles2.clone(), node: Some(node2.clone()) }, + ) + .unwrap(); + cursor + .upsert(block5, &TrieChangeSetsEntry { nibbles: nibbles3.clone(), node: None }) + .unwrap(); + + provider_rw.commit().unwrap(); + } + + // Populate StoragesTrieChangeSets with data across multiple blocks + { + let provider_rw = factory.provider_rw().unwrap(); + let mut cursor = + provider_rw.tx_ref().cursor_dup_write::().unwrap(); + + // Block 100, address1: 2 entries (will be kept - before start block) + let key1_block1 = BlockNumberHashedAddress((block1, storage_address1)); + cursor + .upsert( + key1_block1, + &TrieChangeSetsEntry { nibbles: nibbles1.clone(), node: Some(node1.clone()) }, + ) + .unwrap(); + cursor + .upsert(key1_block1, &TrieChangeSetsEntry { nibbles: nibbles2.clone(), node: None }) + .unwrap(); + + // Block 101, address1: 3 entries with duplicates (will be deleted - from this block + // onwards) + let key1_block2 = BlockNumberHashedAddress((block2, storage_address1)); + cursor + .upsert( + key1_block2, + &TrieChangeSetsEntry { nibbles: nibbles1.clone(), node: Some(node2.clone()) }, + ) + .unwrap(); + cursor + .upsert(key1_block2, &TrieChangeSetsEntry { nibbles: nibbles1.clone(), node: None }) + .unwrap(); // duplicate key + cursor + .upsert( + key1_block2, + &TrieChangeSetsEntry { nibbles: nibbles2.clone(), node: Some(node1.clone()) }, + ) + .unwrap(); + + // Block 102, address2: 2 entries (will be deleted - after start block) + let key2_block3 = BlockNumberHashedAddress((block3, storage_address2)); + cursor + .upsert( + key2_block3, + &TrieChangeSetsEntry { nibbles: nibbles2.clone(), node: Some(node2.clone()) }, + ) + .unwrap(); + cursor + .upsert(key2_block3, &TrieChangeSetsEntry { nibbles: nibbles3.clone(), node: None }) + .unwrap(); + + // Block 103, address1: 2 entries with duplicate (will be deleted - after start block) + let key1_block4 = BlockNumberHashedAddress((block4, storage_address1)); + cursor + .upsert( + key1_block4, + &TrieChangeSetsEntry { nibbles: nibbles3.clone(), node: Some(node1) }, + ) + .unwrap(); + cursor + .upsert( + key1_block4, + &TrieChangeSetsEntry { nibbles: nibbles3, node: Some(node2.clone()) }, + ) + .unwrap(); // duplicate key + + // Block 104, address2: 2 entries (will be deleted - after start block) + let key2_block5 = BlockNumberHashedAddress((block5, storage_address2)); + cursor + .upsert(key2_block5, &TrieChangeSetsEntry { nibbles: nibbles1, node: None }) + .unwrap(); + cursor + .upsert(key2_block5, &TrieChangeSetsEntry { nibbles: nibbles2, node: Some(node2) }) + .unwrap(); + + provider_rw.commit().unwrap(); + } + + // Clear all changesets from block 101 onwards + { + let provider_rw = factory.provider_rw().unwrap(); + provider_rw.clear_trie_changesets_from(block2).unwrap(); + provider_rw.commit().unwrap(); + } + + // Verify AccountsTrieChangeSets after clearing + { + let provider = factory.provider().unwrap(); + let mut cursor = + provider.tx_ref().cursor_dup_read::().unwrap(); + + // Block 100 should still exist (before range) + let block1_entries = cursor + .walk_dup(Some(block1), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert_eq!(block1_entries.len(), 2, "Block 100 entries should be preserved"); + assert_eq!(block1_entries[0].0, block1); + assert_eq!(block1_entries[1].0, block1); + + // Blocks 101-104 should be deleted + let block2_entries = cursor + .walk_dup(Some(block2), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert!(block2_entries.is_empty(), "Block 101 entries should be deleted"); + + let block3_entries = cursor + .walk_dup(Some(block3), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert!(block3_entries.is_empty(), "Block 102 entries should be deleted"); + + let block4_entries = cursor + .walk_dup(Some(block4), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert!(block4_entries.is_empty(), "Block 103 entries should be deleted"); + + // Block 104 should also be deleted + let block5_entries = cursor + .walk_dup(Some(block5), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert!(block5_entries.is_empty(), "Block 104 entries should be deleted"); + } + + // Verify StoragesTrieChangeSets after clearing + { + let provider = factory.provider().unwrap(); + let mut cursor = + provider.tx_ref().cursor_dup_read::().unwrap(); + + // Block 100 entries should still exist (before range) + let key1_block1 = BlockNumberHashedAddress((block1, storage_address1)); + let block1_entries = cursor + .walk_dup(Some(key1_block1), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert_eq!(block1_entries.len(), 2, "Block 100 storage entries should be preserved"); + + // Blocks 101-104 entries should be deleted + let key1_block2 = BlockNumberHashedAddress((block2, storage_address1)); + let block2_entries = cursor + .walk_dup(Some(key1_block2), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert!(block2_entries.is_empty(), "Block 101 storage entries should be deleted"); + + let key2_block3 = BlockNumberHashedAddress((block3, storage_address2)); + let block3_entries = cursor + .walk_dup(Some(key2_block3), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert!(block3_entries.is_empty(), "Block 102 storage entries should be deleted"); + + let key1_block4 = BlockNumberHashedAddress((block4, storage_address1)); + let block4_entries = cursor + .walk_dup(Some(key1_block4), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert!(block4_entries.is_empty(), "Block 103 storage entries should be deleted"); + + // Block 104 entries should also be deleted + let key2_block5 = BlockNumberHashedAddress((block5, storage_address2)); + let block5_entries = cursor + .walk_dup(Some(key2_block5), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert!(block5_entries.is_empty(), "Block 104 storage entries should be deleted"); + } + } + + #[test] + fn test_write_trie_updates_sorted() { + use reth_trie::{ + updates::{StorageTrieUpdatesSorted, TrieUpdatesSorted}, + BranchNodeCompact, StorageTrieEntry, + }; + + let factory = create_test_provider_factory(); + let provider_rw = factory.provider_rw().unwrap(); + + // Pre-populate account trie with data that will be deleted + { + let tx = provider_rw.tx_ref(); + let mut cursor = tx.cursor_write::().unwrap(); + + // Add account node that will be deleted + let to_delete = StoredNibbles(Nibbles::from_nibbles([0x3, 0x4])); + cursor + .upsert( + to_delete, + &BranchNodeCompact::new( + 0b1010_1010_1010_1010, // state_mask + 0b0000_0000_0000_0000, // tree_mask + 0b0000_0000_0000_0000, // hash_mask + vec![], + None, + ), + ) + .unwrap(); + + // Add account node that will be updated + let to_update = StoredNibbles(Nibbles::from_nibbles([0x1, 0x2])); + cursor + .upsert( + to_update, + &BranchNodeCompact::new( + 0b0101_0101_0101_0101, // old state_mask (will be updated) + 0b0000_0000_0000_0000, // tree_mask + 0b0000_0000_0000_0000, // hash_mask + vec![], + None, + ), + ) + .unwrap(); + } + + // Pre-populate storage tries with data + let storage_address1 = B256::from([1u8; 32]); + let storage_address2 = B256::from([2u8; 32]); + { + let tx = provider_rw.tx_ref(); + let mut storage_cursor = tx.cursor_dup_write::().unwrap(); + + // Add storage nodes for address1 (one will be deleted) + storage_cursor + .upsert( + storage_address1, + &StorageTrieEntry { + nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0x2, 0x0])), + node: BranchNodeCompact::new( + 0b0011_0011_0011_0011, // will be deleted + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ), + }, + ) + .unwrap(); + + // Add storage nodes for address2 (will be wiped) + storage_cursor + .upsert( + storage_address2, + &StorageTrieEntry { + nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0xa, 0xb])), + node: BranchNodeCompact::new( + 0b1100_1100_1100_1100, // will be wiped + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ), + }, + ) + .unwrap(); + storage_cursor + .upsert( + storage_address2, + &StorageTrieEntry { + nibbles: StoredNibblesSubKey(Nibbles::from_nibbles([0xc, 0xd])), + node: BranchNodeCompact::new( + 0b0011_1100_0011_1100, // will be wiped + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ), + }, + ) + .unwrap(); + } + + // Create sorted account trie updates + let account_nodes = vec![ + ( + Nibbles::from_nibbles([0x1, 0x2]), + Some(BranchNodeCompact::new( + 0b1111_1111_1111_1111, // state_mask (updated) + 0b0000_0000_0000_0000, // tree_mask + 0b0000_0000_0000_0000, // hash_mask (no hashes) + vec![], + None, + )), + ), + (Nibbles::from_nibbles([0x3, 0x4]), None), // Deletion + ( + Nibbles::from_nibbles([0x5, 0x6]), + Some(BranchNodeCompact::new( + 0b1111_1111_1111_1111, // state_mask + 0b0000_0000_0000_0000, // tree_mask + 0b0000_0000_0000_0000, // hash_mask (no hashes) + vec![], + None, + )), + ), + ]; + + // Create sorted storage trie updates + let storage_trie1 = StorageTrieUpdatesSorted { + is_deleted: false, + storage_nodes: vec![ + ( + Nibbles::from_nibbles([0x1, 0x0]), + Some(BranchNodeCompact::new( + 0b1111_0000_0000_0000, // state_mask + 0b0000_0000_0000_0000, // tree_mask + 0b0000_0000_0000_0000, // hash_mask (no hashes) + vec![], + None, + )), + ), + (Nibbles::from_nibbles([0x2, 0x0]), None), // Deletion of existing node + ], + }; + + let storage_trie2 = StorageTrieUpdatesSorted { + is_deleted: true, // Wipe all storage for this address + storage_nodes: vec![], + }; + + let mut storage_tries = B256Map::default(); + storage_tries.insert(storage_address1, storage_trie1); + storage_tries.insert(storage_address2, storage_trie2); + + let trie_updates = TrieUpdatesSorted { account_nodes, storage_tries }; + + // Write the sorted trie updates + let num_entries = provider_rw.write_trie_updates_sorted(&trie_updates).unwrap(); + + // We should have 2 account insertions + 1 account deletion + 1 storage insertion + 1 + // storage deletion = 5 + assert_eq!(num_entries, 5); + + // Verify account trie updates were written correctly + let tx = provider_rw.tx_ref(); + let mut cursor = tx.cursor_read::().unwrap(); + + // Check first account node was updated + let nibbles1 = StoredNibbles(Nibbles::from_nibbles([0x1, 0x2])); + let entry1 = cursor.seek_exact(nibbles1).unwrap(); + assert!(entry1.is_some(), "Updated account node should exist"); + let expected_mask = reth_trie::TrieMask::new(0b1111_1111_1111_1111); + assert_eq!( + entry1.unwrap().1.state_mask, + expected_mask, + "Account node should have updated state_mask" + ); + + // Check deleted account node no longer exists + let nibbles2 = StoredNibbles(Nibbles::from_nibbles([0x3, 0x4])); + let entry2 = cursor.seek_exact(nibbles2).unwrap(); + assert!(entry2.is_none(), "Deleted account node should not exist"); + + // Check new account node exists + let nibbles3 = StoredNibbles(Nibbles::from_nibbles([0x5, 0x6])); + let entry3 = cursor.seek_exact(nibbles3).unwrap(); + assert!(entry3.is_some(), "New account node should exist"); + + // Verify storage trie updates were written correctly + let mut storage_cursor = tx.cursor_dup_read::().unwrap(); + + // Check storage for address1 + let storage_entries1: Vec<_> = storage_cursor + .walk_dup(Some(storage_address1), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert_eq!( + storage_entries1.len(), + 1, + "Storage address1 should have 1 entry after deletion" + ); + assert_eq!( + storage_entries1[0].1.nibbles.0, + Nibbles::from_nibbles([0x1, 0x0]), + "Remaining entry should be [0x1, 0x0]" + ); + + // Check storage for address2 was wiped + let storage_entries2: Vec<_> = storage_cursor + .walk_dup(Some(storage_address2), None) + .unwrap() + .collect::, _>>() + .unwrap(); + assert_eq!(storage_entries2.len(), 0, "Storage address2 should be empty after wipe"); + + provider_rw.commit().unwrap(); + } + + #[test] + fn test_get_block_trie_updates() { + use reth_db_api::models::BlockNumberHashedAddress; + use reth_trie::{BranchNodeCompact, StorageTrieEntry}; + + let factory = create_test_provider_factory(); + let provider_rw = factory.provider_rw().unwrap(); + + let target_block = 2u64; + let next_block = 3u64; + + // Create test nibbles and nodes for accounts + let account_nibbles1 = Nibbles::from_nibbles([0x1, 0x2, 0x3, 0x4]); + let account_nibbles2 = Nibbles::from_nibbles([0x5, 0x6, 0x7, 0x8]); + let account_nibbles3 = Nibbles::from_nibbles([0x9, 0xa, 0xb, 0xc]); + + let node1 = BranchNodeCompact::new( + 0b1111_1111_0000_0000, + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + let node2 = BranchNodeCompact::new( + 0b0000_0000_1111_1111, + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + let node3 = BranchNodeCompact::new( + 0b1010_1010_1010_1010, + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + // Pre-populate AccountsTrie with nodes that will be the final state + { + let mut cursor = provider_rw.tx_ref().cursor_write::().unwrap(); + cursor.insert(StoredNibbles(account_nibbles1), &node1).unwrap(); + cursor.insert(StoredNibbles(account_nibbles2), &node2).unwrap(); + // account_nibbles3 will be deleted (not in final state) + } + + // Insert trie changesets for target_block + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_write::().unwrap(); + // nibbles1 was updated in target_block (old value stored) + cursor + .append_dup( + target_block, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(account_nibbles1), + node: Some(BranchNodeCompact::new( + 0b1111_0000_0000_0000, // old value + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + )), + }, + ) + .unwrap(); + // nibbles2 was created in target_block (no old value) + cursor + .append_dup( + target_block, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(account_nibbles2), + node: None, + }, + ) + .unwrap(); + } + + // Insert trie changesets for next_block (to test overlay) + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_write::().unwrap(); + // nibbles3 was deleted in next_block (old value stored) + cursor + .append_dup( + next_block, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(account_nibbles3), + node: Some(node3), + }, + ) + .unwrap(); + } + + // Storage trie updates + let storage_address1 = B256::from([1u8; 32]); + let storage_nibbles1 = Nibbles::from_nibbles([0xa, 0xb]); + let storage_nibbles2 = Nibbles::from_nibbles([0xc, 0xd]); + + let storage_node1 = BranchNodeCompact::new( + 0b1111_1111_1111_0000, + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + let storage_node2 = BranchNodeCompact::new( + 0b0101_0101_0101_0101, + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + ); + + // Pre-populate StoragesTrie with final state + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_write::().unwrap(); + cursor + .upsert( + storage_address1, + &StorageTrieEntry { + nibbles: StoredNibblesSubKey(storage_nibbles1), + node: storage_node1.clone(), + }, + ) + .unwrap(); + // storage_nibbles2 was deleted in next_block, so it's not in final state + } + + // Insert storage trie changesets for target_block + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_write::().unwrap(); + let key = BlockNumberHashedAddress((target_block, storage_address1)); + + // storage_nibbles1 was updated + cursor + .append_dup( + key, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles1), + node: Some(BranchNodeCompact::new( + 0b0000_0000_1111_1111, // old value + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + )), + }, + ) + .unwrap(); + + // storage_nibbles2 was created + cursor + .append_dup( + key, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles2), + node: None, + }, + ) + .unwrap(); + } + + // Insert storage trie changesets for next_block (to test overlay) + { + let mut cursor = + provider_rw.tx_ref().cursor_dup_write::().unwrap(); + let key = BlockNumberHashedAddress((next_block, storage_address1)); + + // storage_nibbles2 was deleted in next_block + cursor + .append_dup( + key, + TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey(storage_nibbles2), + node: Some(BranchNodeCompact::new( + 0b0101_0101_0101_0101, // value that was deleted + 0b0000_0000_0000_0000, + 0b0000_0000_0000_0000, + vec![], + None, + )), + }, + ) + .unwrap(); + } + + provider_rw.commit().unwrap(); + + // Now test get_block_trie_updates + let provider = factory.provider().unwrap(); + let result = provider.get_block_trie_updates(target_block).unwrap(); + + // Verify account trie updates + assert_eq!(result.account_nodes.len(), 2, "Should have 2 account trie updates"); + + // Check nibbles1 - should have the current value (node1) + let nibbles1_update = result + .account_nodes + .iter() + .find(|(n, _)| n == &account_nibbles1) + .expect("Should find nibbles1"); + assert!(nibbles1_update.1.is_some(), "nibbles1 should have a value"); + assert_eq!( + nibbles1_update.1.as_ref().unwrap().state_mask, + node1.state_mask, + "nibbles1 should have current value" + ); + + // Check nibbles2 - should have the current value (node2) + let nibbles2_update = result + .account_nodes + .iter() + .find(|(n, _)| n == &account_nibbles2) + .expect("Should find nibbles2"); + assert!(nibbles2_update.1.is_some(), "nibbles2 should have a value"); + assert_eq!( + nibbles2_update.1.as_ref().unwrap().state_mask, + node2.state_mask, + "nibbles2 should have current value" + ); + + // nibbles3 should NOT be in the result (it was changed in next_block, not target_block) + assert!( + !result.account_nodes.iter().any(|(n, _)| n == &account_nibbles3), + "nibbles3 should not be in target_block updates" + ); + + // Verify storage trie updates + assert_eq!(result.storage_tries.len(), 1, "Should have 1 storage trie"); + let storage_updates = result + .storage_tries + .get(&storage_address1) + .expect("Should have storage updates for address1"); + + assert_eq!(storage_updates.storage_nodes.len(), 2, "Should have 2 storage node updates"); + + // Check storage_nibbles1 - should have current value + let storage1_update = storage_updates + .storage_nodes + .iter() + .find(|(n, _)| n == &storage_nibbles1) + .expect("Should find storage_nibbles1"); + assert!(storage1_update.1.is_some(), "storage_nibbles1 should have a value"); + assert_eq!( + storage1_update.1.as_ref().unwrap().state_mask, + storage_node1.state_mask, + "storage_nibbles1 should have current value" + ); + + // Check storage_nibbles2 - was created in target_block, will be deleted in next_block + // So it should have a value (the value that will be deleted) + let storage2_update = storage_updates + .storage_nodes + .iter() + .find(|(n, _)| n == &storage_nibbles2) + .expect("Should find storage_nibbles2"); + assert!( + storage2_update.1.is_some(), + "storage_nibbles2 should have a value (the node that will be deleted in next block)" + ); + assert_eq!( + storage2_update.1.as_ref().unwrap().state_mask, + storage_node2.state_mask, + "storage_nibbles2 should have the value that was created and will be deleted" + ); + } } diff --git a/crates/storage/provider/src/providers/mod.rs b/crates/storage/provider/src/providers/mod.rs index ab54fe01e56..5a950bbd7d2 100644 --- a/crates/storage/provider/src/providers/mod.rs +++ b/crates/storage/provider/src/providers/mod.rs @@ -17,7 +17,7 @@ mod state; pub use state::{ historical::{HistoricalStateProvider, HistoricalStateProviderRef, LowestAvailableBlocks}, latest::{LatestStateProvider, LatestStateProviderRef}, - overlay::OverlayStateProvider, + overlay::{OverlayStateProvider, OverlayStateProviderFactory}, }; mod consistent_view; diff --git a/crates/storage/provider/src/providers/state/overlay.rs b/crates/storage/provider/src/providers/state/overlay.rs index 7e6a40efef2..71c1a693193 100644 --- a/crates/storage/provider/src/providers/state/overlay.rs +++ b/crates/storage/provider/src/providers/state/overlay.rs @@ -1,15 +1,143 @@ -use alloy_primitives::B256; +use alloy_primitives::{BlockNumber, B256}; use reth_db_api::DatabaseError; -use reth_storage_api::DBProvider; +use reth_errors::ProviderError; +use reth_stages_types::StageId; +use reth_storage_api::{DBProvider, DatabaseProviderFactory, StageCheckpointReader, TrieReader}; use reth_trie::{ hashed_cursor::{HashedCursorFactory, HashedPostStateCursorFactory}, trie_cursor::{InMemoryTrieCursorFactory, TrieCursorFactory}, updates::TrieUpdatesSorted, - HashedPostStateSorted, + HashedPostState, HashedPostStateSorted, KeccakKeyHasher, +}; +use reth_trie_db::{ + DatabaseHashedCursorFactory, DatabaseHashedPostState, DatabaseTrieCursorFactory, }; -use reth_trie_db::{DatabaseHashedCursorFactory, DatabaseTrieCursorFactory}; use std::sync::Arc; +/// Factory for creating overlay state providers with optional reverts and overlays. +/// +/// This factory allows building an `OverlayStateProvider` whose DB state has been reverted to a +/// particular block, and/or with additional overlay information added on top. +#[derive(Debug, Clone)] +pub struct OverlayStateProviderFactory { + /// The underlying database provider factory + factory: F, + /// Optional block number for collecting reverts + block_number: Option, + /// Optional trie overlay + trie_overlay: Option>, + /// Optional hashed state overlay + hashed_state_overlay: Option>, +} + +impl OverlayStateProviderFactory +where + F: DatabaseProviderFactory, + F::Provider: Clone + TrieReader + StageCheckpointReader, +{ + /// Create a new overlay state provider factory + pub const fn new(factory: F) -> Self { + Self { factory, block_number: None, trie_overlay: None, hashed_state_overlay: None } + } + + /// Set the block number for collecting reverts + pub const fn with_block_number(mut self, block_number: Option) -> Self { + self.block_number = block_number; + self + } + + /// Set the trie overlay + pub fn with_trie_overlay(mut self, trie_overlay: Option>) -> Self { + self.trie_overlay = trie_overlay; + self + } + + /// Set the hashed state overlay + pub fn with_hashed_state_overlay( + mut self, + hashed_state_overlay: Option>, + ) -> Self { + self.hashed_state_overlay = hashed_state_overlay; + self + } + + /// Validates that there are sufficient changesets to revert to the requested block number. + /// + /// Returns an error if the `MerkleChangeSets` checkpoint doesn't cover the requested block. + fn validate_changesets_availability( + &self, + provider: &F::Provider, + requested_block: BlockNumber, + ) -> Result<(), ProviderError> { + // Get the MerkleChangeSets stage checkpoint - let errors propagate as-is + let checkpoint = provider.get_stage_checkpoint(StageId::MerkleChangeSets)?; + + // If there's no checkpoint at all or block range details are missing, we can't revert + let available_range = checkpoint + .and_then(|chk| { + chk.merkle_changesets_stage_checkpoint() + .map(|stage_chk| stage_chk.block_range.from..=chk.block_number) + }) + .ok_or_else(|| ProviderError::InsufficientChangesets { + requested: requested_block, + available: 0..=0, + })?; + + // Check if the requested block is within the available range + if !available_range.contains(&requested_block) { + return Err(ProviderError::InsufficientChangesets { + requested: requested_block, + available: available_range, + }); + } + + Ok(()) + } + + /// Create a read-only [`OverlayStateProvider`]. + pub fn provider_ro(&self) -> Result, ProviderError> { + // Get a read-only provider + let provider = self.factory.database_provider_ro()?; + + // If block_number is provided, collect reverts + let (trie_updates, hashed_state) = if let Some(from_block) = self.block_number { + // Validate that we have sufficient changesets for the requested block + self.validate_changesets_availability(&provider, from_block)?; + + // Collect trie reverts + let mut trie_updates_mut = provider.trie_reverts(from_block)?; + + // Collect state reverts using HashedPostState::from_reverts + let reverted_state = + HashedPostState::from_reverts::(provider.tx_ref(), from_block..)?; + let mut hashed_state_mut = reverted_state.into_sorted(); + + // Extend with overlays if provided + if let Some(trie_overlay) = &self.trie_overlay { + trie_updates_mut.extend_ref(trie_overlay); + } + + if let Some(hashed_state_overlay) = &self.hashed_state_overlay { + hashed_state_mut.extend_ref(hashed_state_overlay); + } + + (Arc::new(trie_updates_mut), Arc::new(hashed_state_mut)) + } else { + // If no block_number, use overlays directly or defaults + let trie_updates = + self.trie_overlay.clone().unwrap_or_else(|| Arc::new(TrieUpdatesSorted::default())); + let hashed_state = self + .hashed_state_overlay + .clone() + .unwrap_or_else(|| Arc::new(HashedPostStateSorted::default())); + + (trie_updates, hashed_state) + }; + + Ok(OverlayStateProvider::new(provider, trie_updates, hashed_state)) + } +} + /// State provider with in-memory overlay from trie updates and hashed post state. /// /// This provider uses in-memory trie updates and hashed post state as an overlay diff --git a/crates/storage/provider/src/test_utils/mock.rs b/crates/storage/provider/src/test_utils/mock.rs index 1024312ead9..3e33e2b0509 100644 --- a/crates/storage/provider/src/test_utils/mock.rs +++ b/crates/storage/provider/src/test_utils/mock.rs @@ -34,12 +34,13 @@ use reth_stages_types::{StageCheckpoint, StageId}; use reth_storage_api::{ BlockBodyIndicesProvider, BytecodeReader, DBProvider, DatabaseProviderFactory, HashedPostStateProvider, NodePrimitivesProvider, StageCheckpointReader, StateProofProvider, - StorageRootProvider, + StorageRootProvider, TrieReader, }; use reth_storage_errors::provider::{ConsistentViewError, ProviderError, ProviderResult}; use reth_trie::{ - updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, - MultiProofTargets, StorageMultiProof, StorageProof, TrieInput, + updates::{TrieUpdates, TrieUpdatesSorted}, + AccountProof, HashedPostState, HashedStorage, MultiProof, MultiProofTargets, StorageMultiProof, + StorageProof, TrieInput, }; use std::{ collections::BTreeMap, @@ -1005,6 +1006,19 @@ impl StateReader for MockEthProvider< } } +impl TrieReader for MockEthProvider { + fn trie_reverts(&self, _from: BlockNumber) -> ProviderResult { + Ok(TrieUpdatesSorted::default()) + } + + fn get_block_trie_updates( + &self, + _block_number: BlockNumber, + ) -> ProviderResult { + Ok(TrieUpdatesSorted::default()) + } +} + impl CanonStateSubscriptions for MockEthProvider { diff --git a/crates/storage/provider/src/test_utils/mod.rs b/crates/storage/provider/src/test_utils/mod.rs index d65655de8bf..ccda2d60e85 100644 --- a/crates/storage/provider/src/test_utils/mod.rs +++ b/crates/storage/provider/src/test_utils/mod.rs @@ -89,7 +89,7 @@ pub fn insert_genesis>( let (root, updates) = StateRoot::from_tx(provider.tx_ref()) .root_with_updates() .map_err(reth_db::DatabaseError::from)?; - provider.write_trie_updates(&updates).unwrap(); + provider.write_trie_updates(updates).unwrap(); provider.commit()?; diff --git a/crates/storage/provider/src/traits/full.rs b/crates/storage/provider/src/traits/full.rs index 374a35f473c..710ca9400ed 100644 --- a/crates/storage/provider/src/traits/full.rs +++ b/crates/storage/provider/src/traits/full.rs @@ -3,7 +3,7 @@ use crate::{ AccountReader, BlockReader, BlockReaderIdExt, ChainSpecProvider, ChangeSetReader, DatabaseProviderFactory, HashedPostStateProvider, StageCheckpointReader, StateProviderFactory, - StateReader, StaticFileProviderFactory, + StateReader, StaticFileProviderFactory, TrieReader, }; use reth_chain_state::{CanonStateSubscriptions, ForkChoiceSubscriptions}; use reth_node_types::{BlockTy, HeaderTy, NodeTypesWithDB, ReceiptTy, TxTy}; @@ -12,7 +12,7 @@ use std::fmt::Debug; /// Helper trait to unify all provider traits for simplicity. pub trait FullProvider: - DatabaseProviderFactory + DatabaseProviderFactory + NodePrimitivesProvider + StaticFileProviderFactory + BlockReaderIdExt< @@ -37,7 +37,7 @@ pub trait FullProvider: } impl FullProvider for T where - T: DatabaseProviderFactory + T: DatabaseProviderFactory + NodePrimitivesProvider + StaticFileProviderFactory + BlockReaderIdExt< diff --git a/crates/storage/provider/src/writer/mod.rs b/crates/storage/provider/src/writer/mod.rs index 1151990f97b..6d990e17a49 100644 --- a/crates/storage/provider/src/writer/mod.rs +++ b/crates/storage/provider/src/writer/mod.rs @@ -909,7 +909,7 @@ mod tests { } let (_, updates) = StateRoot::from_tx(tx).root_with_updates().unwrap(); - provider_rw.write_trie_updates(&updates).unwrap(); + provider_rw.write_trie_updates(updates).unwrap(); let mut state = State::builder().with_bundle_update().build(); @@ -1127,7 +1127,10 @@ mod tests { assert_eq!(storage_root, storage_root_prehashed(init_storage.storage)); assert!(!storage_updates.is_empty()); provider_rw - .write_storage_trie_updates(core::iter::once((&hashed_address, &storage_updates))) + .write_storage_trie_updates_sorted(core::iter::once(( + &hashed_address, + &storage_updates.into_sorted(), + ))) .unwrap(); // destroy the storage and re-create with new slots diff --git a/crates/storage/storage-api/src/noop.rs b/crates/storage/storage-api/src/noop.rs index e0c57d5226b..6b70a5260a6 100644 --- a/crates/storage/storage-api/src/noop.rs +++ b/crates/storage/storage-api/src/noop.rs @@ -6,7 +6,7 @@ use crate::{ HashedPostStateProvider, HeaderProvider, NodePrimitivesProvider, PruneCheckpointReader, ReceiptProvider, ReceiptProviderIdExt, StageCheckpointReader, StateProofProvider, StateProvider, StateProviderBox, StateProviderFactory, StateReader, StateRootProvider, - StorageRootProvider, TransactionVariant, TransactionsProvider, + StorageRootProvider, TransactionVariant, TransactionsProvider, TrieReader, }; #[cfg(feature = "db-api")] @@ -35,8 +35,9 @@ use reth_prune_types::{PruneCheckpoint, PruneSegment}; use reth_stages_types::{StageCheckpoint, StageId}; use reth_storage_errors::provider::{ProviderError, ProviderResult}; use reth_trie_common::{ - updates::TrieUpdates, AccountProof, HashedPostState, HashedStorage, MultiProof, - MultiProofTargets, StorageMultiProof, StorageProof, TrieInput, + updates::{TrieUpdates, TrieUpdatesSorted}, + AccountProof, HashedPostState, HashedStorage, MultiProof, MultiProofTargets, StorageMultiProof, + StorageProof, TrieInput, }; /// Supports various api interfaces for testing purposes. @@ -59,7 +60,7 @@ impl NoopProvider { #[cfg(feature = "db-api")] tx: TxMock::default(), #[cfg(feature = "db-api")] - prune_modes: PruneModes::none(), + prune_modes: PruneModes::default(), _phantom: Default::default(), } } @@ -73,7 +74,7 @@ impl NoopProvider { #[cfg(feature = "db-api")] tx: TxMock::default(), #[cfg(feature = "db-api")] - prune_modes: PruneModes::none(), + prune_modes: PruneModes::default(), _phantom: Default::default(), } } @@ -646,6 +647,19 @@ impl DBProvider for NoopProvider TrieReader for NoopProvider { + fn trie_reverts(&self, _from: BlockNumber) -> ProviderResult { + Ok(TrieUpdatesSorted::default()) + } + + fn get_block_trie_updates( + &self, + _block_number: BlockNumber, + ) -> ProviderResult { + Ok(TrieUpdatesSorted::default()) + } +} + #[cfg(feature = "db-api")] impl DatabaseProviderFactory for NoopProvider diff --git a/crates/storage/storage-api/src/trie.rs b/crates/storage/storage-api/src/trie.rs index 3f39cf3838d..9ff02c106e5 100644 --- a/crates/storage/storage-api/src/trie.rs +++ b/crates/storage/storage-api/src/trie.rs @@ -1,8 +1,8 @@ use alloc::vec::Vec; -use alloy_primitives::{Address, Bytes, B256}; +use alloy_primitives::{Address, BlockNumber, Bytes, B256}; use reth_storage_errors::provider::ProviderResult; use reth_trie_common::{ - updates::{StorageTrieUpdates, TrieUpdates}, + updates::{StorageTrieUpdatesSorted, TrieUpdates, TrieUpdatesSorted}, AccountProof, HashedPostState, HashedStorage, MultiProof, MultiProofTargets, StorageMultiProof, StorageProof, TrieInput, }; @@ -89,25 +89,93 @@ pub trait StateProofProvider: Send + Sync { fn witness(&self, input: TrieInput, target: HashedPostState) -> ProviderResult>; } +/// Trie Reader +#[auto_impl::auto_impl(&, Arc, Box)] +pub trait TrieReader: Send + Sync { + /// Returns the [`TrieUpdatesSorted`] for reverting the trie database to its state prior to the + /// given block and onwards having been processed. + fn trie_reverts(&self, from: BlockNumber) -> ProviderResult; + + /// Returns the trie updates that were applied by the specified block. + fn get_block_trie_updates( + &self, + block_number: BlockNumber, + ) -> ProviderResult; +} + /// Trie Writer #[auto_impl::auto_impl(&, Arc, Box)] pub trait TrieWriter: Send + Sync { /// Writes trie updates to the database. /// /// Returns the number of entries modified. - fn write_trie_updates(&self, trie_updates: &TrieUpdates) -> ProviderResult; + fn write_trie_updates(&self, trie_updates: TrieUpdates) -> ProviderResult { + self.write_trie_updates_sorted(&trie_updates.into_sorted()) + } + + /// Writes trie updates to the database with already sorted updates. + /// + /// Returns the number of entries modified. + fn write_trie_updates_sorted(&self, trie_updates: &TrieUpdatesSorted) -> ProviderResult; + + /// Records the current values of all trie nodes which will be updated using the [`TrieUpdates`] + /// into the trie changesets tables. + /// + /// The intended usage of this method is to call it _prior_ to calling `write_trie_updates` with + /// the same [`TrieUpdates`]. + /// + /// The `updates_overlay` parameter allows providing additional in-memory trie updates that + /// should be considered when looking up current node values. When provided, these overlay + /// updates are applied on top of the database state, allowing the method to see a view that + /// includes both committed database values and pending in-memory changes. This is useful + /// when writing changesets for updates that depend on previous uncommitted trie changes. + /// + /// Returns the number of keys written. + fn write_trie_changesets( + &self, + block_number: BlockNumber, + trie_updates: &TrieUpdatesSorted, + updates_overlay: Option<&TrieUpdatesSorted>, + ) -> ProviderResult; + + /// Clears contents of trie changesets completely + fn clear_trie_changesets(&self) -> ProviderResult<()>; + + /// Clears contents of trie changesets starting from the given block number (inclusive) onwards. + fn clear_trie_changesets_from(&self, from: BlockNumber) -> ProviderResult<()>; } /// Storage Trie Writer #[auto_impl::auto_impl(&, Arc, Box)] pub trait StorageTrieWriter: Send + Sync { - /// Writes storage trie updates from the given storage trie map. + /// Writes storage trie updates from the given storage trie map with already sorted updates. /// - /// First sorts the storage trie updates by the hashed address key, writing in sorted order. + /// Expects the storage trie updates to already be sorted by the hashed address key. /// /// Returns the number of entries modified. - fn write_storage_trie_updates<'a>( + fn write_storage_trie_updates_sorted<'a>( + &self, + storage_tries: impl Iterator, + ) -> ProviderResult; + + /// Records the current values of all trie nodes which will be updated using the + /// [`StorageTrieUpdatesSorted`] into the storage trie changesets table. + /// + /// The intended usage of this method is to call it _prior_ to calling + /// `write_storage_trie_updates` with the same set of [`StorageTrieUpdatesSorted`]. + /// + /// The `updates_overlay` parameter allows providing additional in-memory trie updates that + /// should be considered when looking up current node values. When provided, these overlay + /// updates are applied on top of the database state for each storage trie, allowing the + /// method to see a view that includes both committed database values and pending in-memory + /// changes. This is useful when writing changesets for storage updates that depend on + /// previous uncommitted trie changes. + /// + /// Returns the number of keys written. + fn write_storage_trie_changesets<'a>( &self, - storage_tries: impl Iterator, + block_number: BlockNumber, + storage_tries: impl Iterator, + updates_overlay: Option<&TrieUpdatesSorted>, ) -> ProviderResult; } diff --git a/crates/tracing/src/layers.rs b/crates/tracing/src/layers.rs index 385c4fac51d..d27bbc96b6e 100644 --- a/crates/tracing/src/layers.rs +++ b/crates/tracing/src/layers.rs @@ -130,13 +130,13 @@ impl Layers { &mut self, service_name: String, endpoint_exporter: url::Url, - level: tracing::Level, + filter: EnvFilter, ) -> eyre::Result<()> { // Create the span provider let span_layer = span_layer(service_name, &endpoint_exporter) .map_err(|e| eyre::eyre!("Failed to build OTLP span exporter {}", e))? - .with_filter(tracing::level_filters::LevelFilter::from_level(level)); + .with_filter(filter); self.add_layer(span_layer); diff --git a/crates/transaction-pool/src/blobstore/converter.rs b/crates/transaction-pool/src/blobstore/converter.rs new file mode 100644 index 00000000000..3f6abc56bff --- /dev/null +++ b/crates/transaction-pool/src/blobstore/converter.rs @@ -0,0 +1,30 @@ +use alloy_consensus::{BlobTransactionSidecar, EnvKzgSettings}; +use alloy_eips::eip7594::BlobTransactionSidecarEip7594; +use tokio::sync::Semaphore; + +// We allow up to 5 concurrent conversions to avoid excessive memory usage. +static SEMAPHORE: Semaphore = Semaphore::const_new(5); + +/// A simple semaphore-based blob sidecar converter. +#[derive(Debug, Clone, Default)] +#[non_exhaustive] +pub struct BlobSidecarConverter; + +impl BlobSidecarConverter { + /// Creates a new blob sidecar converter. + pub const fn new() -> Self { + Self + } + + /// Converts the blob sidecar to the EIP-7594 format. + pub async fn convert( + &self, + sidecar: BlobTransactionSidecar, + ) -> Option { + let _permit = SEMAPHORE.acquire().await.ok()?; + tokio::task::spawn_blocking(move || sidecar.try_into_7594(EnvKzgSettings::Default.get())) + .await + .ok()? + .ok() + } +} diff --git a/crates/transaction-pool/src/blobstore/disk.rs b/crates/transaction-pool/src/blobstore/disk.rs index 5ccafe15000..b883345aac6 100644 --- a/crates/transaction-pool/src/blobstore/disk.rs +++ b/crates/transaction-pool/src/blobstore/disk.rs @@ -4,6 +4,8 @@ use crate::blobstore::{BlobStore, BlobStoreCleanupStat, BlobStoreError, BlobStor use alloy_eips::{ eip4844::{BlobAndProofV1, BlobAndProofV2}, eip7594::BlobTransactionSidecarVariant, + eip7840::BlobParams, + merge::EPOCH_SLOTS, }; use alloy_primitives::{TxHash, B256}; use parking_lot::{Mutex, RwLock}; @@ -14,6 +16,13 @@ use tracing::{debug, trace}; /// How many [`BlobTransactionSidecarVariant`] to cache in memory. pub const DEFAULT_MAX_CACHED_BLOBS: u32 = 100; +/// A cache size heuristic based on the highest blob params +/// +/// This uses the max blobs per tx and max blobs per block over 16 epochs: `21 * 6 * 512 = 64512` +/// This should be ~4MB +const VERSIONED_HASH_TO_TX_HASH_CACHE_SIZE: u64 = + BlobParams::bpo2().max_blobs_per_tx * BlobParams::bpo2().max_blob_count * EPOCH_SLOTS * 16; + /// A blob store that stores blob data on disk. /// /// The type uses deferred deletion, meaning that blobs are not immediately deleted from disk, but @@ -288,7 +297,9 @@ impl DiskFileBlobStoreInner { size_tracker: Default::default(), file_lock: Default::default(), txs_to_delete: Default::default(), - versioned_hashes_to_txhash: Mutex::new(LruMap::new(ByLength::new(max_length * 6))), + versioned_hashes_to_txhash: Mutex::new(LruMap::new(ByLength::new( + VERSIONED_HASH_TO_TX_HASH_CACHE_SIZE as u32, + ))), } } diff --git a/crates/transaction-pool/src/blobstore/mod.rs b/crates/transaction-pool/src/blobstore/mod.rs index 29844994bc0..ee7eb45af0f 100644 --- a/crates/transaction-pool/src/blobstore/mod.rs +++ b/crates/transaction-pool/src/blobstore/mod.rs @@ -5,6 +5,7 @@ use alloy_eips::{ eip7594::BlobTransactionSidecarVariant, }; use alloy_primitives::B256; +pub use converter::BlobSidecarConverter; pub use disk::{DiskFileBlobStore, DiskFileBlobStoreConfig, OpenDiskFileBlobStore}; pub use mem::InMemoryBlobStore; pub use noop::NoopBlobStore; @@ -17,6 +18,7 @@ use std::{ }; pub use tracker::{BlobStoreCanonTracker, BlobStoreUpdates}; +mod converter; pub mod disk; mod mem; mod noop; diff --git a/crates/transaction-pool/src/maintain.rs b/crates/transaction-pool/src/maintain.rs index 732d55d0c3f..aa0366341a6 100644 --- a/crates/transaction-pool/src/maintain.rs +++ b/crates/transaction-pool/src/maintain.rs @@ -1,11 +1,12 @@ //! Support for maintaining the state of the transaction pool use crate::{ - blobstore::{BlobStoreCanonTracker, BlobStoreUpdates}, + blobstore::{BlobSidecarConverter, BlobStoreCanonTracker, BlobStoreUpdates}, error::PoolError, metrics::MaintainPoolMetrics, traits::{CanonicalStateUpdate, EthPoolTransaction, TransactionPool, TransactionPoolExt}, - BlockInfo, PoolTransaction, PoolUpdateKind, TransactionOrigin, + AllPoolTransactions, BlobTransactionSidecarVariant, BlockInfo, PoolTransaction, PoolUpdateKind, + TransactionOrigin, }; use alloy_consensus::{transaction::TxHashRef, BlockHeader, Typed2718}; use alloy_eips::{BlockNumberOrTag, Decodable2718, Encodable2718}; @@ -16,7 +17,7 @@ use futures_util::{ FutureExt, Stream, StreamExt, }; use reth_chain_state::CanonStateNotification; -use reth_chainspec::{ChainSpecProvider, EthChainSpec}; +use reth_chainspec::{ChainSpecProvider, EthChainSpec, EthereumHardforks}; use reth_execution_types::ChangedAccount; use reth_fs_util::FsPathError; use reth_primitives_traits::{ @@ -103,12 +104,12 @@ where N: NodePrimitives, Client: StateProviderFactory + BlockReaderIdExt
- + ChainSpecProvider> + + ChainSpecProvider + EthereumHardforks> + Clone + 'static, P: TransactionPoolExt> + 'static, St: Stream> + Send + Unpin + 'static, - Tasks: TaskSpawner + 'static, + Tasks: TaskSpawner + Clone + 'static, { async move { maintain_transaction_pool(client, pool, events, task_spawner, config).await; @@ -129,12 +130,12 @@ pub async fn maintain_transaction_pool( N: NodePrimitives, Client: StateProviderFactory + BlockReaderIdExt
- + ChainSpecProvider> + + ChainSpecProvider + EthereumHardforks> + Clone + 'static, P: TransactionPoolExt> + 'static, St: Stream> + Send + Unpin + 'static, - Tasks: TaskSpawner + 'static, + Tasks: TaskSpawner + Clone + 'static, { let metrics = MaintainPoolMetrics::default(); let MaintainPoolConfig { max_update_depth, max_reload_accounts, .. } = config; @@ -494,6 +495,89 @@ pub async fn maintain_transaction_pool( // keep track of mined blob transactions blob_store_tracker.add_new_chain_blocks(&blocks); + + // If Osaka activates in 2 slots we need to convert blobs to new format. + if !chain_spec.is_osaka_active_at_timestamp(tip.timestamp()) && + !chain_spec.is_osaka_active_at_timestamp(tip.timestamp().saturating_add(12)) && + chain_spec.is_osaka_active_at_timestamp(tip.timestamp().saturating_add(24)) + { + let pool = pool.clone(); + let spawner = task_spawner.clone(); + let client = client.clone(); + task_spawner.spawn(Box::pin(async move { + // Start converting not eaerlier than 4 seconds into current slot to ensure + // that our pool only contains valid transactions for the next block (as + // it's not Osaka yet). + tokio::time::sleep(Duration::from_secs(4)).await; + + let mut interval = tokio::time::interval(Duration::from_secs(1)); + loop { + // Loop and replace blob transactions until we reach Osaka transition + // block after which no legacy blobs are going to be accepted. + let last_iteration = + client.latest_header().ok().flatten().is_none_or(|header| { + client + .chain_spec() + .is_osaka_active_at_timestamp(header.timestamp()) + }); + + let AllPoolTransactions { pending, queued } = pool.all_transactions(); + for tx in pending + .into_iter() + .chain(queued) + .filter(|tx| tx.transaction.is_eip4844()) + { + let tx_hash = *tx.transaction.hash(); + + // Fetch sidecar from the pool + let Ok(Some(sidecar)) = pool.get_blob(tx_hash) else { + continue; + }; + // Ensure it is a legacy blob + if !sidecar.is_eip4844() { + continue; + } + // Remove transaction and sidecar from the pool, both are in memory + // now + let Some(tx) = pool.remove_transactions(vec![tx_hash]).pop() else { + continue; + }; + pool.delete_blob(tx_hash); + + let BlobTransactionSidecarVariant::Eip4844(sidecar) = + Arc::unwrap_or_clone(sidecar) + else { + continue; + }; + + let converter = BlobSidecarConverter::new(); + let pool = pool.clone(); + spawner.spawn(Box::pin(async move { + // Convert sidecar to EIP-7594 format + let Some(sidecar) = converter.convert(sidecar).await else { + return; + }; + + // Re-insert transaction with the new sidecar + let origin = tx.origin; + let Some(tx) = EthPoolTransaction::try_from_eip4844( + tx.transaction.clone_into_consensus(), + sidecar.into(), + ) else { + return; + }; + let _ = pool.add_transaction(origin, tx).await; + })); + } + + if last_iteration { + break; + } + + interval.tick().await; + } + })); + } } } } diff --git a/crates/transaction-pool/src/test_utils/pool.rs b/crates/transaction-pool/src/test_utils/pool.rs index 6af440f086a..ab7bebae2f5 100644 --- a/crates/transaction-pool/src/test_utils/pool.rs +++ b/crates/transaction-pool/src/test_utils/pool.rs @@ -188,7 +188,7 @@ pub(crate) enum Scenario { HigherNonce { onchain: u64, nonce: u64 }, Multi { // Execute multiple test scenarios - scenario: Vec, + scenario: Vec, }, } diff --git a/crates/transaction-pool/src/traits.rs b/crates/transaction-pool/src/traits.rs index 9552646652b..2b9d8bae8ab 100644 --- a/crates/transaction-pool/src/traits.rs +++ b/crates/transaction-pool/src/traits.rs @@ -429,6 +429,20 @@ pub trait TransactionPool: Clone + Debug + Send + Sync { /// Consumer: Utility fn all_transaction_hashes(&self) -> Vec; + /// Removes a single transaction corresponding to the given hash. + /// + /// Note: This removes the transaction as if it got discarded (_not_ mined). + /// + /// Returns the removed transaction if it was found in the pool. + /// + /// Consumer: Utility + fn remove_transaction( + &self, + hash: TxHash, + ) -> Option>> { + self.remove_transactions(vec![hash]).pop() + } + /// Removes all transactions corresponding to the given hashes. /// /// Note: This removes the transactions as if they got discarded (_not_ mined). diff --git a/crates/transaction-pool/src/validate/eth.rs b/crates/transaction-pool/src/validate/eth.rs index 6d1a0147f0b..038c820bfe9 100644 --- a/crates/transaction-pool/src/validate/eth.rs +++ b/crates/transaction-pool/src/validate/eth.rs @@ -39,7 +39,7 @@ use std::{ atomic::{AtomicBool, AtomicU64}, Arc, }, - time::Instant, + time::{Instant, SystemTime}, }; use tokio::sync::Mutex; @@ -396,7 +396,7 @@ where // max possible tx fee is (gas_price * gas_limit) // (if EIP1559) max possible tx fee is (max_fee_per_gas * gas_limit) let gas_price = transaction.max_fee_per_gas(); - let max_tx_fee_wei = gas_price.saturating_mul(transaction.gas_limit() as u128); + let max_tx_fee_wei = gas_price.saturating_mul(transaction_gas_limit as u128); if max_tx_fee_wei > tx_fee_cap_wei { return Err(TransactionValidationOutcome::Invalid( transaction, @@ -673,7 +673,7 @@ where Eip4844PoolTransactionError::UnexpectedEip4844SidecarAfterOsaka, )) } - } else if sidecar.is_eip7594() { + } else if sidecar.is_eip7594() && !self.allow_7594_sidecars() { return Err(InvalidPoolTransactionError::Eip4844( Eip4844PoolTransactionError::UnexpectedEip7594SidecarBeforeOsaka, )) @@ -745,6 +745,10 @@ where self.fork_tracker.osaka.store(true, std::sync::atomic::Ordering::Relaxed); } + self.fork_tracker + .tip_timestamp + .store(new_tip_block.timestamp(), std::sync::atomic::Ordering::Relaxed); + if let Some(blob_params) = self.chain_spec().blob_params_at_timestamp(new_tip_block.timestamp()) { @@ -759,6 +763,24 @@ where fn max_gas_limit(&self) -> u64 { self.block_gas_limit.load(std::sync::atomic::Ordering::Relaxed) } + + /// Returns whether EIP-7594 sidecars are allowed + fn allow_7594_sidecars(&self) -> bool { + let tip_timestamp = self.fork_tracker.tip_timestamp(); + + // If next block is Osaka, allow 7594 sidecars + if self.chain_spec().is_osaka_active_at_timestamp(tip_timestamp.saturating_add(12)) { + true + } else if self.chain_spec().is_osaka_active_at_timestamp(tip_timestamp.saturating_add(24)) { + let current_timestamp = + SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs(); + + // Allow after 4 seconds into last non-Osaka slot + current_timestamp >= tip_timestamp.saturating_add(4) + } else { + false + } + } } impl TransactionValidator for EthTransactionValidator @@ -811,6 +833,8 @@ pub struct EthTransactionValidatorBuilder { prague: bool, /// Fork indicator whether we are in the Osaka hardfork. osaka: bool, + /// Timestamp of the tip block. + tip_timestamp: u64, /// Max blob count at the block's timestamp. max_blob_count: u64, /// Whether using EIP-2718 type transactions is allowed @@ -885,6 +909,8 @@ impl EthTransactionValidatorBuilder { // osaka not yet activated osaka: false, + tip_timestamp: 0, + // max blob count is prague by default max_blob_count: BlobParams::prague().max_blobs_per_tx, @@ -1012,6 +1038,7 @@ impl EthTransactionValidatorBuilder { self.cancun = self.client.chain_spec().is_cancun_active_at_timestamp(timestamp); self.prague = self.client.chain_spec().is_prague_active_at_timestamp(timestamp); self.osaka = self.client.chain_spec().is_osaka_active_at_timestamp(timestamp); + self.tip_timestamp = timestamp; self.max_blob_count = self .client .chain_spec() @@ -1072,6 +1099,7 @@ impl EthTransactionValidatorBuilder { cancun, prague, osaka, + tip_timestamp, eip2718, eip1559, eip4844, @@ -1094,6 +1122,7 @@ impl EthTransactionValidatorBuilder { cancun: AtomicBool::new(cancun), prague: AtomicBool::new(prague), osaka: AtomicBool::new(osaka), + tip_timestamp: AtomicU64::new(tip_timestamp), max_blob_count: AtomicU64::new(max_blob_count), }; @@ -1175,6 +1204,8 @@ pub struct ForkTracker { pub osaka: AtomicBool, /// Tracks max blob count per transaction at the block's timestamp. pub max_blob_count: AtomicU64, + /// Tracks the timestamp of the tip block. + pub tip_timestamp: AtomicU64, } impl ForkTracker { @@ -1198,6 +1229,11 @@ impl ForkTracker { self.osaka.load(std::sync::atomic::Ordering::Relaxed) } + /// Returns the timestamp of the tip block. + pub fn tip_timestamp(&self) -> u64 { + self.tip_timestamp.load(std::sync::atomic::Ordering::Relaxed) + } + /// Returns the max allowed blob count per transaction. pub fn max_blob_count(&self) -> u64 { self.max_blob_count.load(std::sync::atomic::Ordering::Relaxed) @@ -1272,6 +1308,7 @@ mod tests { cancun: false.into(), prague: false.into(), osaka: false.into(), + tip_timestamp: 0.into(), max_blob_count: 0.into(), }; diff --git a/crates/trie/common/src/hashed_state.rs b/crates/trie/common/src/hashed_state.rs index 50d9f20af0b..27c2807ad2a 100644 --- a/crates/trie/common/src/hashed_state.rs +++ b/crates/trie/common/src/hashed_state.rs @@ -3,6 +3,7 @@ use core::ops::Not; use crate::{ added_removed_keys::MultiAddedRemovedKeys, prefix_set::{PrefixSetMut, TriePrefixSetsMut}, + utils::extend_sorted_vec, KeyHasher, MultiProofTargets, Nibbles, }; use alloc::{borrow::Cow, vec::Vec}; @@ -484,6 +485,21 @@ impl HashedPostStateSorted { pub const fn account_storages(&self) -> &B256Map { &self.storages } + + /// Extends this state with contents of another sorted state. + /// Entries in `other` take precedence for duplicate keys. + pub fn extend_ref(&mut self, other: &Self) { + // Extend accounts + self.accounts.extend_ref(&other.accounts); + + // Extend storages + for (hashed_address, other_storage) in &other.storages { + self.storages + .entry(*hashed_address) + .and_modify(|existing| existing.extend_ref(other_storage)) + .or_insert_with(|| other_storage.clone()); + } + } } impl AsRef for HashedPostStateSorted { @@ -510,6 +526,20 @@ impl HashedAccountsSorted { .chain(self.destroyed_accounts.iter().map(|address| (*address, None))) .sorted_by_key(|entry| *entry.0) } + + /// Extends this collection with contents of another sorted collection. + /// Entries in `other` take precedence for duplicate keys. + pub fn extend_ref(&mut self, other: &Self) { + // Updates take precedence over removals, so we want removals from `other` to only apply to + // the previous accounts. + self.accounts.retain(|(addr, _)| !other.destroyed_accounts.contains(addr)); + + // Extend the sorted accounts vector + extend_sorted_vec(&mut self.accounts, &other.accounts); + + // Merge destroyed accounts sets + self.destroyed_accounts.extend(&other.destroyed_accounts); + } } /// Sorted hashed storage optimized for iterating during state trie calculation. @@ -537,6 +567,28 @@ impl HashedStorageSorted { .chain(self.zero_valued_slots.iter().map(|hashed_slot| (*hashed_slot, U256::ZERO))) .sorted_by_key(|entry| *entry.0) } + + /// Extends this storage with contents of another sorted storage. + /// Entries in `other` take precedence for duplicate keys. + pub fn extend_ref(&mut self, other: &Self) { + if other.wiped { + // If other is wiped, clear everything and copy from other + self.wiped = true; + self.non_zero_valued_slots.clear(); + self.zero_valued_slots.clear(); + self.non_zero_valued_slots.extend_from_slice(&other.non_zero_valued_slots); + self.zero_valued_slots.extend(&other.zero_valued_slots); + return; + } + + self.non_zero_valued_slots.retain(|(slot, _)| !other.zero_valued_slots.contains(slot)); + + // Extend the sorted non-zero valued slots + extend_sorted_vec(&mut self.non_zero_valued_slots, &other.non_zero_valued_slots); + + // Merge zero valued slots sets + self.zero_valued_slots.extend(&other.zero_valued_slots); + } } /// An iterator that yields chunks of the state updates of at most `size` account and storage @@ -1072,4 +1124,102 @@ mod tests { ); assert_eq!(chunks.next(), None); } + + #[test] + fn test_hashed_post_state_sorted_extend_ref() { + // Test extending accounts + let mut state1 = HashedPostStateSorted { + accounts: HashedAccountsSorted { + accounts: vec![ + (B256::from([1; 32]), Account::default()), + (B256::from([3; 32]), Account::default()), + ], + destroyed_accounts: B256Set::from_iter([B256::from([5; 32])]), + }, + storages: B256Map::default(), + }; + + let state2 = HashedPostStateSorted { + accounts: HashedAccountsSorted { + accounts: vec![ + (B256::from([2; 32]), Account::default()), + (B256::from([3; 32]), Account { nonce: 1, ..Default::default() }), // Override + (B256::from([4; 32]), Account::default()), + ], + destroyed_accounts: B256Set::from_iter([B256::from([6; 32])]), + }, + storages: B256Map::default(), + }; + + state1.extend_ref(&state2); + + // Check accounts are merged and sorted + assert_eq!(state1.accounts.accounts.len(), 4); + assert_eq!(state1.accounts.accounts[0].0, B256::from([1; 32])); + assert_eq!(state1.accounts.accounts[1].0, B256::from([2; 32])); + assert_eq!(state1.accounts.accounts[2].0, B256::from([3; 32])); + assert_eq!(state1.accounts.accounts[2].1.nonce, 1); // Should have state2's value + assert_eq!(state1.accounts.accounts[3].0, B256::from([4; 32])); + + // Check destroyed accounts are merged + assert!(state1.accounts.destroyed_accounts.contains(&B256::from([5; 32]))); + assert!(state1.accounts.destroyed_accounts.contains(&B256::from([6; 32]))); + } + + #[test] + fn test_hashed_storage_sorted_extend_ref() { + // Test normal extension + let mut storage1 = HashedStorageSorted { + non_zero_valued_slots: vec![ + (B256::from([1; 32]), U256::from(10)), + (B256::from([3; 32]), U256::from(30)), + ], + zero_valued_slots: B256Set::from_iter([B256::from([5; 32])]), + wiped: false, + }; + + let storage2 = HashedStorageSorted { + non_zero_valued_slots: vec![ + (B256::from([2; 32]), U256::from(20)), + (B256::from([3; 32]), U256::from(300)), // Override + (B256::from([4; 32]), U256::from(40)), + ], + zero_valued_slots: B256Set::from_iter([B256::from([6; 32])]), + wiped: false, + }; + + storage1.extend_ref(&storage2); + + assert_eq!(storage1.non_zero_valued_slots.len(), 4); + assert_eq!(storage1.non_zero_valued_slots[0].0, B256::from([1; 32])); + assert_eq!(storage1.non_zero_valued_slots[1].0, B256::from([2; 32])); + assert_eq!(storage1.non_zero_valued_slots[2].0, B256::from([3; 32])); + assert_eq!(storage1.non_zero_valued_slots[2].1, U256::from(300)); // Should have storage2's value + assert_eq!(storage1.non_zero_valued_slots[3].0, B256::from([4; 32])); + assert!(storage1.zero_valued_slots.contains(&B256::from([5; 32]))); + assert!(storage1.zero_valued_slots.contains(&B256::from([6; 32]))); + assert!(!storage1.wiped); + + // Test wiped storage + let mut storage3 = HashedStorageSorted { + non_zero_valued_slots: vec![(B256::from([1; 32]), U256::from(10))], + zero_valued_slots: B256Set::from_iter([B256::from([2; 32])]), + wiped: false, + }; + + let storage4 = HashedStorageSorted { + non_zero_valued_slots: vec![(B256::from([3; 32]), U256::from(30))], + zero_valued_slots: B256Set::from_iter([B256::from([4; 32])]), + wiped: true, + }; + + storage3.extend_ref(&storage4); + + assert!(storage3.wiped); + // When wiped, should only have storage4's values + assert_eq!(storage3.non_zero_valued_slots.len(), 1); + assert_eq!(storage3.non_zero_valued_slots[0].0, B256::from([3; 32])); + assert_eq!(storage3.zero_valued_slots.len(), 1); + assert!(storage3.zero_valued_slots.contains(&B256::from([4; 32]))); + } } diff --git a/crates/trie/common/src/input.rs b/crates/trie/common/src/input.rs index fff50fbb7b0..522cfa9ed41 100644 --- a/crates/trie/common/src/input.rs +++ b/crates/trie/common/src/input.rs @@ -34,7 +34,7 @@ impl TrieInput { /// Create new trie input from the provided blocks, from oldest to newest. See the documentation /// for [`Self::extend_with_blocks`] for details. pub fn from_blocks<'a>( - blocks: impl IntoIterator)>, + blocks: impl IntoIterator, ) -> Self { let mut input = Self::default(); input.extend_with_blocks(blocks); @@ -47,14 +47,10 @@ impl TrieInput { /// constructed from the state of this block and the state itself, **without** trie updates. pub fn extend_with_blocks<'a>( &mut self, - blocks: impl IntoIterator)>, + blocks: impl IntoIterator, ) { for (hashed_state, trie_updates) in blocks { - if let Some(nodes) = trie_updates.as_ref() { - self.append_cached_ref(nodes, hashed_state); - } else { - self.append_ref(hashed_state); - } + self.append_cached_ref(trie_updates, hashed_state); } } diff --git a/crates/trie/common/src/lib.rs b/crates/trie/common/src/lib.rs index 70616ba5eb8..e4292a52016 100644 --- a/crates/trie/common/src/lib.rs +++ b/crates/trie/common/src/lib.rs @@ -36,7 +36,7 @@ mod nibbles; pub use nibbles::{Nibbles, StoredNibbles, StoredNibblesSubKey}; mod storage; -pub use storage::StorageTrieEntry; +pub use storage::{StorageTrieEntry, TrieChangeSetsEntry}; mod subnode; pub use subnode::StoredSubNode; @@ -57,6 +57,9 @@ pub mod updates; pub mod added_removed_keys; +/// Utilities used by other modules in this crate. +mod utils; + /// Bincode-compatible serde implementations for trie types. /// /// `bincode` crate allows for more efficient serialization of trie types, because it allows diff --git a/crates/trie/common/src/prefix_set.rs b/crates/trie/common/src/prefix_set.rs index 6714893f16d..35c4bc67839 100644 --- a/crates/trie/common/src/prefix_set.rs +++ b/crates/trie/common/src/prefix_set.rs @@ -280,8 +280,8 @@ mod tests { prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6])); prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate - assert_eq!(prefix_set_mut.keys.len(), 4); // Length should be 3 (including duplicate) - assert_eq!(prefix_set_mut.keys.capacity(), 4); // Capacity should be 4 (including duplicate) + assert_eq!(prefix_set_mut.keys.len(), 4); // Length is 4 (before deduplication) + assert_eq!(prefix_set_mut.keys.capacity(), 4); // Capacity is 4 (before deduplication) let mut prefix_set = prefix_set_mut.freeze(); assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([1, 2]))); @@ -300,8 +300,8 @@ mod tests { prefix_set_mut.insert(Nibbles::from_nibbles([4, 5, 6])); prefix_set_mut.insert(Nibbles::from_nibbles([1, 2, 3])); // Duplicate - assert_eq!(prefix_set_mut.keys.len(), 4); // Length should be 3 (including duplicate) - assert_eq!(prefix_set_mut.keys.capacity(), 101); // Capacity should be 101 (including duplicate) + assert_eq!(prefix_set_mut.keys.len(), 4); // Length is 4 (before deduplication) + assert_eq!(prefix_set_mut.keys.capacity(), 101); // Capacity is 101 (before deduplication) let mut prefix_set = prefix_set_mut.freeze(); assert!(prefix_set.contains(&Nibbles::from_nibbles_unchecked([1, 2]))); diff --git a/crates/trie/common/src/storage.rs b/crates/trie/common/src/storage.rs index 187a097bfd4..557b9e4a606 100644 --- a/crates/trie/common/src/storage.rs +++ b/crates/trie/common/src/storage.rs @@ -1,6 +1,8 @@ -use super::{BranchNodeCompact, StoredNibblesSubKey}; +use super::{BranchNodeCompact, Nibbles, StoredNibblesSubKey}; /// Account storage trie node. +/// +/// `nibbles` is the subkey when used as a value in the `StorageTrie` table. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))] pub struct StorageTrieEntry { @@ -31,3 +33,173 @@ impl reth_codecs::Compact for StorageTrieEntry { (this, buf) } } + +/// Trie changeset entry representing the state of a trie node before a block. +/// +/// `nibbles` is the subkey when used as a value in the changeset tables. +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize, serde::Deserialize))] +pub struct TrieChangeSetsEntry { + /// The nibbles of the intermediate node + pub nibbles: StoredNibblesSubKey, + /// Node value prior to the block being processed, None indicating it didn't exist. + pub node: Option, +} + +#[cfg(any(test, feature = "reth-codec"))] +impl reth_codecs::Compact for TrieChangeSetsEntry { + fn to_compact(&self, buf: &mut B) -> usize + where + B: bytes::BufMut + AsMut<[u8]>, + { + let nibbles_len = self.nibbles.to_compact(buf); + let node_len = self.node.as_ref().map(|node| node.to_compact(buf)).unwrap_or(0); + nibbles_len + node_len + } + + fn from_compact(buf: &[u8], len: usize) -> (Self, &[u8]) { + if len == 0 { + // Return an empty entry without trying to parse anything + return ( + Self { nibbles: StoredNibblesSubKey::from(Nibbles::default()), node: None }, + buf, + ) + } + + let (nibbles, buf) = StoredNibblesSubKey::from_compact(buf, 65); + + if len <= 65 { + return (Self { nibbles, node: None }, buf) + } + + let (node, buf) = BranchNodeCompact::from_compact(buf, len - 65); + (Self { nibbles, node: Some(node) }, buf) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use bytes::BytesMut; + use reth_codecs::Compact; + + #[test] + fn test_trie_changesets_entry_full_empty() { + // Test a fully empty entry (empty nibbles, None node) + let entry = TrieChangeSetsEntry { nibbles: StoredNibblesSubKey::from(vec![]), node: None }; + + let mut buf = BytesMut::new(); + let len = entry.to_compact(&mut buf); + + // Empty nibbles takes 65 bytes (64 for padding + 1 for length) + // None node adds 0 bytes + assert_eq!(len, 65); + assert_eq!(buf.len(), 65); + + // Deserialize and verify + let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, len); + assert_eq!(decoded.nibbles.0.to_vec(), Vec::::new()); + assert_eq!(decoded.node, None); + assert_eq!(remaining.len(), 0); + } + + #[test] + fn test_trie_changesets_entry_none_node() { + // Test non-empty nibbles with None node + let nibbles_data = vec![0x01, 0x02, 0x03, 0x04]; + let entry = TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey::from(nibbles_data.clone()), + node: None, + }; + + let mut buf = BytesMut::new(); + let len = entry.to_compact(&mut buf); + + // Nibbles takes 65 bytes regardless of content + assert_eq!(len, 65); + + // Deserialize and verify + let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, len); + assert_eq!(decoded.nibbles.0.to_vec(), nibbles_data); + assert_eq!(decoded.node, None); + assert_eq!(remaining.len(), 0); + } + + #[test] + fn test_trie_changesets_entry_empty_path_with_node() { + // Test empty path with Some node + // Using the same signature as in the codebase: (state_mask, hash_mask, tree_mask, hashes, + // value) + let test_node = BranchNodeCompact::new( + 0b1111_1111_1111_1111, // state_mask: all children present + 0b1111_1111_1111_1111, // hash_mask: all have hashes + 0b0000_0000_0000_0000, // tree_mask: no embedded trees + vec![], // hashes + None, // value + ); + + let entry = TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey::from(vec![]), + node: Some(test_node.clone()), + }; + + let mut buf = BytesMut::new(); + let len = entry.to_compact(&mut buf); + + // Calculate expected length + let mut temp_buf = BytesMut::new(); + let node_len = test_node.to_compact(&mut temp_buf); + assert_eq!(len, 65 + node_len); + + // Deserialize and verify + let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, len); + assert_eq!(decoded.nibbles.0.to_vec(), Vec::::new()); + assert_eq!(decoded.node, Some(test_node)); + assert_eq!(remaining.len(), 0); + } + + #[test] + fn test_trie_changesets_entry_normal() { + // Test normal case: non-empty path with Some node + let nibbles_data = vec![0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]; + // Using the same signature as in the codebase + let test_node = BranchNodeCompact::new( + 0b0000_0000_1111_0000, // state_mask: some children present + 0b0000_0000_0011_0000, // hash_mask: some have hashes + 0b0000_0000_0000_0000, // tree_mask: no embedded trees + vec![], // hashes (empty for this test) + None, // value + ); + + let entry = TrieChangeSetsEntry { + nibbles: StoredNibblesSubKey::from(nibbles_data.clone()), + node: Some(test_node.clone()), + }; + + let mut buf = BytesMut::new(); + let len = entry.to_compact(&mut buf); + + // Verify serialization length + let mut temp_buf = BytesMut::new(); + let node_len = test_node.to_compact(&mut temp_buf); + assert_eq!(len, 65 + node_len); + + // Deserialize and verify + let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, len); + assert_eq!(decoded.nibbles.0.to_vec(), nibbles_data); + assert_eq!(decoded.node, Some(test_node)); + assert_eq!(remaining.len(), 0); + } + + #[test] + fn test_trie_changesets_entry_from_compact_zero_len() { + // Test from_compact with zero length + let buf = vec![0x01, 0x02, 0x03]; + let (decoded, remaining) = TrieChangeSetsEntry::from_compact(&buf, 0); + + // Should return empty nibbles and None node + assert_eq!(decoded.nibbles.0.to_vec(), Vec::::new()); + assert_eq!(decoded.node, None); + assert_eq!(remaining, &buf[..]); // Buffer should be unchanged + } +} diff --git a/crates/trie/common/src/updates.rs b/crates/trie/common/src/updates.rs index 441e407db16..00a160c4f9f 100644 --- a/crates/trie/common/src/updates.rs +++ b/crates/trie/common/src/updates.rs @@ -1,4 +1,4 @@ -use crate::{BranchNodeCompact, HashBuilder, Nibbles}; +use crate::{utils::extend_sorted_vec, BranchNodeCompact, HashBuilder, Nibbles}; use alloc::{ collections::{btree_map::BTreeMap, btree_set::BTreeSet}, vec::Vec, @@ -438,6 +438,11 @@ pub struct TrieUpdatesSorted { } impl TrieUpdatesSorted { + /// Returns `true` if the updates are empty. + pub fn is_empty(&self) -> bool { + self.account_nodes.is_empty() && self.storage_tries.is_empty() + } + /// Returns reference to updated account nodes. pub fn account_nodes_ref(&self) -> &[(Nibbles, Option)] { &self.account_nodes @@ -447,6 +452,24 @@ impl TrieUpdatesSorted { pub const fn storage_tries_ref(&self) -> &B256Map { &self.storage_tries } + + /// Extends the trie updates with another set of sorted updates. + /// + /// This merges the account nodes and storage tries from `other` into `self`. + /// Account nodes are merged and re-sorted, with `other`'s values taking precedence + /// for duplicate keys. + pub fn extend_ref(&mut self, other: &Self) { + // Extend account nodes + extend_sorted_vec(&mut self.account_nodes, &other.account_nodes); + + // Merge storage tries + for (hashed_address, storage_trie) in &other.storage_tries { + self.storage_tries + .entry(*hashed_address) + .and_modify(|existing| existing.extend_ref(storage_trie)) + .or_insert_with(|| storage_trie.clone()); + } + } } impl AsRef for TrieUpdatesSorted { @@ -455,6 +478,29 @@ impl AsRef for TrieUpdatesSorted { } } +impl From for TrieUpdates { + fn from(sorted: TrieUpdatesSorted) -> Self { + let mut account_nodes = HashMap::default(); + let mut removed_nodes = HashSet::default(); + + for (nibbles, node) in sorted.account_nodes { + if let Some(node) = node { + account_nodes.insert(nibbles, node); + } else { + removed_nodes.insert(nibbles); + } + } + + let storage_tries = sorted + .storage_tries + .into_iter() + .map(|(address, storage)| (address, storage.into())) + .collect(); + + Self { account_nodes, removed_nodes, storage_tries } + } +} + /// Sorted storage trie updates reference used for serializing to file. #[derive(PartialEq, Eq, Clone, Default, Debug)] #[cfg_attr(any(test, feature = "serde"), derive(serde::Serialize))] @@ -488,6 +534,23 @@ impl StorageTrieUpdatesSorted { pub fn storage_nodes_ref(&self) -> &[(Nibbles, Option)] { &self.storage_nodes } + + /// Extends the storage trie updates with another set of sorted updates. + /// + /// If `other` is marked as deleted, this will be marked as deleted and all nodes cleared. + /// Otherwise, nodes are merged with `other`'s values taking precedence for duplicates. + pub fn extend_ref(&mut self, other: &Self) { + if other.is_deleted { + self.is_deleted = true; + self.storage_nodes.clear(); + self.storage_nodes.extend(other.storage_nodes.iter().cloned()); + return; + } + + // Extend storage nodes + extend_sorted_vec(&mut self.storage_nodes, &other.storage_nodes); + self.is_deleted = self.is_deleted || other.is_deleted; + } } /// Excludes empty nibbles from the given iterator. @@ -502,6 +565,153 @@ fn exclude_empty_from_pair( iter.into_iter().filter(|(n, _)| !n.is_empty()) } +impl From for StorageTrieUpdates { + fn from(sorted: StorageTrieUpdatesSorted) -> Self { + let mut storage_nodes = HashMap::default(); + let mut removed_nodes = HashSet::default(); + + for (nibbles, node) in sorted.storage_nodes { + if let Some(node) = node { + storage_nodes.insert(nibbles, node); + } else { + removed_nodes.insert(nibbles); + } + } + + Self { is_deleted: sorted.is_deleted, storage_nodes, removed_nodes } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use alloy_primitives::B256; + + #[test] + fn test_trie_updates_sorted_extend_ref() { + // Test extending with empty updates + let mut updates1 = TrieUpdatesSorted::default(); + let updates2 = TrieUpdatesSorted::default(); + updates1.extend_ref(&updates2); + assert_eq!(updates1.account_nodes.len(), 0); + assert_eq!(updates1.storage_tries.len(), 0); + + // Test extending account nodes + let mut updates1 = TrieUpdatesSorted { + account_nodes: vec![ + (Nibbles::from_nibbles_unchecked([0x01]), Some(BranchNodeCompact::default())), + (Nibbles::from_nibbles_unchecked([0x03]), None), + ], + storage_tries: B256Map::default(), + }; + let updates2 = TrieUpdatesSorted { + account_nodes: vec![ + (Nibbles::from_nibbles_unchecked([0x02]), Some(BranchNodeCompact::default())), + (Nibbles::from_nibbles_unchecked([0x03]), Some(BranchNodeCompact::default())), /* Override */ + ], + storage_tries: B256Map::default(), + }; + updates1.extend_ref(&updates2); + assert_eq!(updates1.account_nodes.len(), 3); + // Should be sorted: 0x01, 0x02, 0x03 + assert_eq!(updates1.account_nodes[0].0, Nibbles::from_nibbles_unchecked([0x01])); + assert_eq!(updates1.account_nodes[1].0, Nibbles::from_nibbles_unchecked([0x02])); + assert_eq!(updates1.account_nodes[2].0, Nibbles::from_nibbles_unchecked([0x03])); + // 0x03 should have Some value from updates2 (override) + assert!(updates1.account_nodes[2].1.is_some()); + + // Test extending storage tries + let storage_trie1 = StorageTrieUpdatesSorted { + is_deleted: false, + storage_nodes: vec![( + Nibbles::from_nibbles_unchecked([0x0a]), + Some(BranchNodeCompact::default()), + )], + }; + let storage_trie2 = StorageTrieUpdatesSorted { + is_deleted: false, + storage_nodes: vec![(Nibbles::from_nibbles_unchecked([0x0b]), None)], + }; + + let hashed_address1 = B256::from([1; 32]); + let hashed_address2 = B256::from([2; 32]); + + let mut updates1 = TrieUpdatesSorted { + account_nodes: vec![], + storage_tries: B256Map::from_iter([(hashed_address1, storage_trie1.clone())]), + }; + let updates2 = TrieUpdatesSorted { + account_nodes: vec![], + storage_tries: B256Map::from_iter([ + (hashed_address1, storage_trie2), + (hashed_address2, storage_trie1), + ]), + }; + updates1.extend_ref(&updates2); + assert_eq!(updates1.storage_tries.len(), 2); + assert!(updates1.storage_tries.contains_key(&hashed_address1)); + assert!(updates1.storage_tries.contains_key(&hashed_address2)); + // Check that storage trie for hashed_address1 was extended + let merged_storage = &updates1.storage_tries[&hashed_address1]; + assert_eq!(merged_storage.storage_nodes.len(), 2); + } + + #[test] + fn test_storage_trie_updates_sorted_extend_ref_deleted() { + // Test case 1: Extending with a deleted storage trie that has nodes + let mut storage1 = StorageTrieUpdatesSorted { + is_deleted: false, + storage_nodes: vec![ + (Nibbles::from_nibbles_unchecked([0x01]), Some(BranchNodeCompact::default())), + (Nibbles::from_nibbles_unchecked([0x02]), None), + ], + }; + + let storage2 = StorageTrieUpdatesSorted { + is_deleted: true, + storage_nodes: vec![ + (Nibbles::from_nibbles_unchecked([0x03]), Some(BranchNodeCompact::default())), + (Nibbles::from_nibbles_unchecked([0x04]), None), + ], + }; + + storage1.extend_ref(&storage2); + + // Should be marked as deleted + assert!(storage1.is_deleted); + // Original nodes should be cleared, but other's nodes should be added + assert_eq!(storage1.storage_nodes.len(), 2); + assert_eq!(storage1.storage_nodes[0].0, Nibbles::from_nibbles_unchecked([0x03])); + assert_eq!(storage1.storage_nodes[1].0, Nibbles::from_nibbles_unchecked([0x04])); + + // Test case 2: Extending a deleted storage trie with more nodes + let mut storage3 = StorageTrieUpdatesSorted { + is_deleted: true, + storage_nodes: vec![( + Nibbles::from_nibbles_unchecked([0x05]), + Some(BranchNodeCompact::default()), + )], + }; + + let storage4 = StorageTrieUpdatesSorted { + is_deleted: true, + storage_nodes: vec![ + (Nibbles::from_nibbles_unchecked([0x06]), Some(BranchNodeCompact::default())), + (Nibbles::from_nibbles_unchecked([0x07]), None), + ], + }; + + storage3.extend_ref(&storage4); + + // Should remain deleted + assert!(storage3.is_deleted); + // Should have nodes from other (original cleared then extended) + assert_eq!(storage3.storage_nodes.len(), 2); + assert_eq!(storage3.storage_nodes[0].0, Nibbles::from_nibbles_unchecked([0x06])); + assert_eq!(storage3.storage_nodes[1].0, Nibbles::from_nibbles_unchecked([0x07])); + } +} + /// Bincode-compatible trie updates type serde implementations. #[cfg(feature = "serde-bincode-compat")] pub mod serde_bincode_compat { @@ -717,7 +927,7 @@ pub mod serde_bincode_compat { } #[cfg(all(test, feature = "serde"))] -mod tests { +mod serde_tests { use super::*; #[test] diff --git a/crates/trie/common/src/utils.rs b/crates/trie/common/src/utils.rs new file mode 100644 index 00000000000..e5d16d3ef51 --- /dev/null +++ b/crates/trie/common/src/utils.rs @@ -0,0 +1,53 @@ +use alloc::vec::Vec; + +/// Helper function to extend a sorted vector with another sorted vector. +/// Values from `other` take precedence for duplicate keys. +/// +/// This function efficiently merges two sorted vectors by: +/// 1. Iterating through the target vector with mutable references +/// 2. Using a peekable iterator for the other vector +/// 3. For each target item, processing other items that come before or equal to it +/// 4. Collecting items from other that need to be inserted +/// 5. Appending and re-sorting only if new items were added +pub(crate) fn extend_sorted_vec(target: &mut Vec<(K, V)>, other: &[(K, V)]) +where + K: Clone + Ord + core::hash::Hash + Eq, + V: Clone, +{ + if other.is_empty() { + return; + } + + let mut other_iter = other.iter().peekable(); + let mut to_insert = Vec::new(); + + // Iterate through target and update/collect items from other + for target_item in target.iter_mut() { + while let Some(other_item) = other_iter.peek() { + use core::cmp::Ordering; + match other_item.0.cmp(&target_item.0) { + Ordering::Less => { + // Other item comes before current target item, collect it + to_insert.push(other_iter.next().unwrap().clone()); + } + Ordering::Equal => { + // Same key, update target with other's value + target_item.1 = other_iter.next().unwrap().1.clone(); + break; + } + Ordering::Greater => { + // Other item comes after current target item, keep target unchanged + break; + } + } + } + } + + // Append collected new items, as well as any remaining from `other` which are necessarily also + // new, and sort if needed + if !to_insert.is_empty() || other_iter.peek().is_some() { + target.extend(to_insert); + target.extend(other_iter.cloned()); + target.sort_unstable_by(|a, b| a.0.cmp(&b.0)); + } +} diff --git a/crates/trie/db/src/state.rs b/crates/trie/db/src/state.rs index 256ee20794e..6d37c5f3413 100644 --- a/crates/trie/db/src/state.rs +++ b/crates/trie/db/src/state.rs @@ -20,7 +20,7 @@ use std::{ collections::HashMap, ops::{RangeBounds, RangeInclusive}, }; -use tracing::debug; +use tracing::{debug, instrument}; /// Extends [`StateRoot`] with operations specific for working with a database transaction. pub trait DatabaseStateRoot<'a, TX>: Sized { @@ -226,6 +226,7 @@ impl<'a, TX: DbTx> DatabaseStateRoot<'a, TX> } impl DatabaseHashedPostState for HashedPostState { + #[instrument(target = "trie::db", skip(tx), fields(range))] fn from_reverts( tx: &TX, range: impl RangeBounds, diff --git a/crates/trie/db/src/trie_cursor.rs b/crates/trie/db/src/trie_cursor.rs index 62d376d1b54..b1e9032fc0f 100644 --- a/crates/trie/db/src/trie_cursor.rs +++ b/crates/trie/db/src/trie_cursor.rs @@ -7,7 +7,7 @@ use reth_db_api::{ }; use reth_trie::{ trie_cursor::{TrieCursor, TrieCursorFactory}, - updates::StorageTrieUpdates, + updates::StorageTrieUpdatesSorted, BranchNodeCompact, Nibbles, StorageTrieEntry, StoredNibbles, StoredNibblesSubKey, }; @@ -110,31 +110,19 @@ where + DbDupCursorRO + DbDupCursorRW, { - /// Writes storage updates - pub fn write_storage_trie_updates( + /// Writes storage updates that are already sorted + pub fn write_storage_trie_updates_sorted( &mut self, - updates: &StorageTrieUpdates, + updates: &StorageTrieUpdatesSorted, ) -> Result { // The storage trie for this account has to be deleted. if updates.is_deleted() && self.cursor.seek_exact(self.hashed_address)?.is_some() { self.cursor.delete_current_duplicates()?; } - // Merge updated and removed nodes. Updated nodes must take precedence. - let mut storage_updates = updates - .removed_nodes_ref() - .iter() - .filter_map(|n| (!updates.storage_nodes_ref().contains_key(n)).then_some((n, None))) - .collect::>(); - storage_updates.extend( - updates.storage_nodes_ref().iter().map(|(nibbles, node)| (nibbles, Some(node))), - ); - - // Sort trie node updates. - storage_updates.sort_unstable_by(|a, b| a.0.cmp(b.0)); - let mut num_entries = 0; - for (nibbles, maybe_updated) in storage_updates.into_iter().filter(|(n, _)| !n.is_empty()) { + for (nibbles, maybe_updated) in updates.storage_nodes.iter().filter(|(n, _)| !n.is_empty()) + { num_entries += 1; let nibbles = StoredNibblesSubKey(*nibbles); // Delete the old entry if it exists. diff --git a/crates/trie/db/tests/trie.rs b/crates/trie/db/tests/trie.rs index e9fcb5a1c48..8f543a711d8 100644 --- a/crates/trie/db/tests/trie.rs +++ b/crates/trie/db/tests/trie.rs @@ -81,7 +81,11 @@ fn incremental_vs_full_root(inputs: &[&str], modified: &str) { let modified_root = loader.root().unwrap(); // Update the intermediate roots table so that we can run the incremental verification - tx.write_storage_trie_updates(core::iter::once((&hashed_address, &trie_updates))).unwrap(); + tx.write_storage_trie_updates_sorted(core::iter::once(( + &hashed_address, + &trie_updates.into_sorted(), + ))) + .unwrap(); // 3. Calculate the incremental root let mut storage_changes = PrefixSetMut::default(); @@ -620,7 +624,7 @@ fn account_trie_around_extension_node_with_dbtrie() { let (got, updates) = StateRoot::from_tx(tx.tx_ref()).root_with_updates().unwrap(); assert_eq!(expected, got); - tx.write_trie_updates(&updates).unwrap(); + tx.write_trie_updates(updates).unwrap(); // read the account updates from the db let mut accounts_trie = tx.tx_ref().cursor_read::().unwrap(); @@ -667,7 +671,7 @@ proptest! { state.iter().map(|(&key, &balance)| (key, (Account { balance, ..Default::default() }, std::iter::empty()))) ); assert_eq!(expected_root, state_root); - tx.write_trie_updates(&trie_updates).unwrap(); + tx.write_trie_updates(trie_updates).unwrap(); } } } diff --git a/crates/trie/parallel/benches/root.rs b/crates/trie/parallel/benches/root.rs index fe1953b9055..48657cc8a70 100644 --- a/crates/trie/parallel/benches/root.rs +++ b/crates/trie/parallel/benches/root.rs @@ -33,7 +33,7 @@ pub fn calculate_state_root(c: &mut Criterion) { provider_rw.write_hashed_state(&db_state.into_sorted()).unwrap(); let (_, updates) = StateRoot::from_tx(provider_rw.tx_ref()).root_with_updates().unwrap(); - provider_rw.write_trie_updates(&updates).unwrap(); + provider_rw.write_trie_updates(updates).unwrap(); provider_rw.commit().unwrap(); } diff --git a/crates/trie/parallel/src/proof.rs b/crates/trie/parallel/src/proof.rs index 0f29502f8c7..3ea5994488a 100644 --- a/crates/trie/parallel/src/proof.rs +++ b/crates/trie/parallel/src/proof.rs @@ -101,7 +101,7 @@ impl ParallelProof { ); self.proof_worker_handle - .queue_storage_proof(input) + .dispatch_storage_proof(input) .map_err(|e| ParallelStateRootError::Other(e.to_string())) } @@ -193,7 +193,7 @@ impl ParallelProof { let receiver = self .proof_worker_handle - .queue_account_multiproof(input) + .dispatch_account_multiproof(input) .map_err(|e| ParallelStateRootError::Other(e.to_string()))?; // Wait for account multiproof result from worker @@ -307,7 +307,7 @@ mod tests { let task_ctx = ProofTaskCtx::new(Default::default(), Default::default(), Default::default()); let proof_worker_handle = - ProofWorkerHandle::new(rt.handle().clone(), consistent_view, task_ctx, 1, 1).unwrap(); + ProofWorkerHandle::new(rt.handle().clone(), consistent_view, task_ctx, 1, 1); let parallel_result = ParallelProof::new( Default::default(), diff --git a/crates/trie/parallel/src/proof_task.rs b/crates/trie/parallel/src/proof_task.rs index 2d0f7e933c8..b3269f21fbb 100644 --- a/crates/trie/parallel/src/proof_task.rs +++ b/crates/trie/parallel/src/proof_task.rs @@ -29,7 +29,6 @@ use reth_db_api::transaction::DbTx; use reth_execution_errors::{SparseTrieError, SparseTrieErrorKind}; use reth_provider::{ providers::ConsistentDbView, BlockReader, DBProvider, DatabaseProviderFactory, ProviderError, - ProviderResult, }; use reth_storage_errors::db::DatabaseError; use reth_trie::{ @@ -112,14 +111,20 @@ enum StorageWorkerJob { /// # Shutdown /// /// Worker shuts down when the crossbeam channel closes (all senders dropped). -fn storage_worker_loop( - proof_tx: ProofTaskTx, +fn storage_worker_loop( + view: ConsistentDbView, + task_ctx: ProofTaskCtx, work_rx: CrossbeamReceiver, worker_id: usize, #[cfg(feature = "metrics")] metrics: ProofTaskTrieMetrics, ) where - Tx: DbTx, + Factory: DatabaseProviderFactory, { + // Create db transaction before entering work loop + let provider = + view.provider_ro().expect("Storage worker failed to initialize: database unavailable"); + let proof_tx = ProofTaskTx::new(provider.into_tx(), task_ctx, worker_id); + tracing::debug!( target: "trie::proof_task", worker_id, @@ -258,15 +263,21 @@ fn storage_worker_loop( /// # Shutdown /// /// Worker shuts down when the crossbeam channel closes (all senders dropped). -fn account_worker_loop( - proof_tx: ProofTaskTx, +fn account_worker_loop( + view: ConsistentDbView, + task_ctx: ProofTaskCtx, work_rx: CrossbeamReceiver, storage_work_tx: CrossbeamSender, worker_id: usize, #[cfg(feature = "metrics")] metrics: ProofTaskTrieMetrics, ) where - Tx: DbTx, + Factory: DatabaseProviderFactory, { + // Create db transaction before entering work loop + let provider = + view.provider_ro().expect("Account worker failed to initialize: database unavailable"); + let proof_tx = ProofTaskTx::new(provider.into_tx(), task_ctx, worker_id); + tracing::debug!( target: "trie::proof_task", worker_id, @@ -308,7 +319,7 @@ fn account_worker_loop( ); tracker.set_precomputed_storage_roots(storage_root_targets_len as u64); - let storage_proof_receivers = match queue_storage_proofs( + let storage_proof_receivers = match dispatch_storage_proofs( &storage_work_tx, &input.targets, &mut storage_prefix_sets, @@ -568,7 +579,7 @@ where /// computation. This enables interleaved parallelism for better performance. /// /// Propagates errors up if queuing fails. Receivers must be consumed by the caller. -fn queue_storage_proofs( +fn dispatch_storage_proofs( storage_work_tx: &CrossbeamSender, targets: &MultiProofTargets, storage_prefix_sets: &mut B256Map, @@ -682,7 +693,7 @@ where multi_added_removed_keys.unwrap_or_else(|| Arc::new(MultiAddedRemovedKeys::new())); let added_removed_keys = multi_added_removed_keys.get_storage(&hashed_address); - let span = tracing::trace_span!( + let span = tracing::info_span!( target: "trie::proof_task", "Storage proof calculation", hashed_address = ?hashed_address, @@ -864,9 +875,9 @@ impl ProofWorkerHandle { task_ctx: ProofTaskCtx, storage_worker_count: usize, account_worker_count: usize, - ) -> ProviderResult + ) -> Self where - Factory: DatabaseProviderFactory, + Factory: DatabaseProviderFactory + Clone + 'static, { let (storage_work_tx, storage_work_rx) = unbounded::(); let (account_work_tx, account_work_rx) = unbounded::(); @@ -880,9 +891,8 @@ impl ProofWorkerHandle { // Spawn storage workers for worker_id in 0..storage_worker_count { - let provider_ro = view.provider_ro()?; - let tx = provider_ro.into_tx(); - let proof_task_tx = ProofTaskTx::new(tx, task_ctx.clone(), worker_id); + let view_clone = view.clone(); + let task_ctx_clone = task_ctx.clone(); let work_rx_clone = storage_work_rx.clone(); executor.spawn_blocking(move || { @@ -890,7 +900,8 @@ impl ProofWorkerHandle { let metrics = ProofTaskTrieMetrics::default(); storage_worker_loop( - proof_task_tx, + view_clone, + task_ctx_clone, work_rx_clone, worker_id, #[cfg(feature = "metrics")] @@ -907,9 +918,8 @@ impl ProofWorkerHandle { // Spawn account workers for worker_id in 0..account_worker_count { - let provider_ro = view.provider_ro()?; - let tx = provider_ro.into_tx(); - let proof_task_tx = ProofTaskTx::new(tx, task_ctx.clone(), worker_id); + let view_clone = view.clone(); + let task_ctx_clone = task_ctx.clone(); let work_rx_clone = account_work_rx.clone(); let storage_work_tx_clone = storage_work_tx.clone(); @@ -918,7 +928,8 @@ impl ProofWorkerHandle { let metrics = ProofTaskTrieMetrics::default(); account_worker_loop( - proof_task_tx, + view_clone, + task_ctx_clone, work_rx_clone, storage_work_tx_clone, worker_id, @@ -934,7 +945,7 @@ impl ProofWorkerHandle { ); } - Ok(Self::new_handle(storage_work_tx, account_work_tx)) + Self::new_handle(storage_work_tx, account_work_tx) } /// Creates a new [`ProofWorkerHandle`] with direct access to worker pools. @@ -947,8 +958,8 @@ impl ProofWorkerHandle { Self { storage_work_tx, account_work_tx } } - /// Queue a storage proof computation - pub fn queue_storage_proof( + /// Dispatch a storage proof computation to storage worker pool + pub fn dispatch_storage_proof( &self, input: StorageProofInput, ) -> Result, ProviderError> { @@ -963,7 +974,7 @@ impl ProofWorkerHandle { } /// Queue an account multiproof computation - pub fn queue_account_multiproof( + pub fn dispatch_account_multiproof( &self, input: AccountMultiproofInput, ) -> Result, ProviderError> { @@ -977,8 +988,8 @@ impl ProofWorkerHandle { Ok(rx) } - /// Internal: Queue blinded storage node request - fn queue_blinded_storage_node( + /// Dispatch blinded storage node request to storage worker pool + pub(crate) fn dispatch_blinded_storage_node( &self, account: B256, path: Nibbles, @@ -993,8 +1004,8 @@ impl ProofWorkerHandle { Ok(rx) } - /// Internal: Queue blinded account node request - fn queue_blinded_account_node( + /// Dispatch blinded account node request to account worker pool + pub(crate) fn dispatch_blinded_account_node( &self, path: Nibbles, ) -> Result, ProviderError> { @@ -1044,13 +1055,13 @@ impl TrieNodeProvider for ProofTaskTrieNodeProvider { match self { Self::AccountNode { handle } => { let rx = handle - .queue_blinded_account_node(*path) + .dispatch_blinded_account_node(*path) .map_err(|error| SparseTrieErrorKind::Other(Box::new(error)))?; rx.recv().map_err(|error| SparseTrieErrorKind::Other(Box::new(error)))? } Self::StorageNode { handle, account } => { let rx = handle - .queue_blinded_storage_node(*account, *path) + .dispatch_blinded_storage_node(*account, *path) .map_err(|error| SparseTrieErrorKind::Other(Box::new(error)))?; rx.recv().map_err(|error| SparseTrieErrorKind::Other(Box::new(error)))? } @@ -1091,7 +1102,7 @@ mod tests { let view = ConsistentDbView::new(factory, None); let ctx = test_ctx(); - let proof_handle = ProofWorkerHandle::new(handle.clone(), view, ctx, 5, 3).unwrap(); + let proof_handle = ProofWorkerHandle::new(handle.clone(), view, ctx, 5, 3); // Verify handle can be cloned let _cloned_handle = proof_handle.clone(); diff --git a/crates/trie/sparse-parallel/src/lower.rs b/crates/trie/sparse-parallel/src/lower.rs index 449c3a7b29b..b5454dd3970 100644 --- a/crates/trie/sparse-parallel/src/lower.rs +++ b/crates/trie/sparse-parallel/src/lower.rs @@ -106,4 +106,20 @@ impl LowerSparseSubtrie { Self::Revealed(_) | Self::Blind(_) => None, } } + + /// Returns the capacity of any maps containing trie nodes + pub(crate) fn node_capacity(&self) -> usize { + match self { + Self::Revealed(trie) | Self::Blind(Some(trie)) => trie.node_capacity(), + Self::Blind(None) => 0, + } + } + + /// Returns the capacity of any maps containing trie values + pub(crate) fn value_capacity(&self) -> usize { + match self { + Self::Revealed(trie) | Self::Blind(Some(trie)) => trie.value_capacity(), + Self::Blind(None) => 0, + } + } } diff --git a/crates/trie/sparse-parallel/src/trie.rs b/crates/trie/sparse-parallel/src/trie.rs index 50c9a79bd05..b15eb7f4edb 100644 --- a/crates/trie/sparse-parallel/src/trie.rs +++ b/crates/trie/sparse-parallel/src/trie.rs @@ -741,13 +741,24 @@ impl SparseTrieInterface for ParallelSparseTrie { // Update subtrie hashes in parallel { use rayon::iter::{IntoParallelIterator, ParallelIterator}; + use tracing::info_span; + let (tx, rx) = mpsc::channel(); let branch_node_tree_masks = &self.branch_node_tree_masks; let branch_node_hash_masks = &self.branch_node_hash_masks; + let span = tracing::Span::current(); changed_subtries .into_par_iter() .map(|mut changed_subtrie| { + let _enter = info_span!( + target: "trie::sparse::parallel", + parent: span.clone(), + "subtrie", + index = changed_subtrie.index + ) + .entered(); + #[cfg(feature = "metrics")] let start = std::time::Instant::now(); changed_subtrie.subtrie.update_hashes( @@ -862,6 +873,16 @@ impl SparseTrieInterface for ParallelSparseTrie { } } } + + fn node_capacity(&self) -> usize { + self.upper_subtrie.node_capacity() + + self.lower_subtries.iter().map(|trie| trie.node_capacity()).sum::() + } + + fn value_capacity(&self) -> usize { + self.upper_subtrie.value_capacity() + + self.lower_subtries.iter().map(|trie| trie.value_capacity()).sum::() + } } impl ParallelSparseTrie { @@ -1282,6 +1303,7 @@ impl ParallelSparseTrie { /// Drains any [`SparseTrieUpdatesAction`]s from the given subtrie, and applies each action to /// the given `updates` set. If the given set is None then this is a no-op. + #[instrument(target = "trie::sparse::parallel", skip_all)] fn apply_subtrie_update_actions( &mut self, update_actions: impl Iterator, @@ -1305,7 +1327,7 @@ impl ParallelSparseTrie { } /// Updates hashes for the upper subtrie, using nodes from both upper and lower subtries. - #[instrument(level = "trace", target = "trie::parallel_sparse", skip_all, ret)] + #[instrument(target = "trie::parallel_sparse", skip_all, ret(level = "trace"))] fn update_upper_subtrie_hashes(&mut self, prefix_set: &mut PrefixSet) -> RlpNode { trace!(target: "trie::parallel_sparse", "Updating upper subtrie hashes"); @@ -1383,6 +1405,7 @@ impl ParallelSparseTrie { /// /// IMPORTANT: The method removes the subtries from `lower_subtries`, and the caller is /// responsible for returning them back into the array. + #[instrument(target = "trie::sparse::parallel", skip_all, fields(prefix_set_len = prefix_set.len()))] fn take_changed_lower_subtries( &mut self, prefix_set: &mut PrefixSet, @@ -1539,6 +1562,7 @@ impl ParallelSparseTrie { /// Return updated subtries back to the trie after executing any actions required on the /// top-level `SparseTrieUpdates`. + #[instrument(target = "trie::sparse::parallel", skip_all)] fn insert_changed_subtries( &mut self, changed_subtries: impl IntoIterator, @@ -2026,7 +2050,7 @@ impl SparseSubtrie { /// # Panics /// /// If the node at the root path does not exist. - #[instrument(level = "trace", target = "trie::parallel_sparse", skip_all, fields(root = ?self.path), ret)] + #[instrument(target = "trie::parallel_sparse", skip_all, fields(root = ?self.path), ret(level = "trace"))] fn update_hashes( &mut self, prefix_set: &mut PrefixSet, @@ -2077,6 +2101,16 @@ impl SparseSubtrie { self.nodes.clear(); self.inner.clear(); } + + /// Returns the capacity of the map containing trie nodes. + pub(crate) fn node_capacity(&self) -> usize { + self.nodes.capacity() + } + + /// Returns the capacity of the map containing trie values. + pub(crate) fn value_capacity(&self) -> usize { + self.inner.value_capacity() + } } /// Helper type for [`SparseSubtrie`] to mutably access only a subset of fields from the original @@ -2410,6 +2444,11 @@ impl SparseSubtrieInner { self.values.clear(); self.buffers.clear(); } + + /// Returns the capacity of the map storing leaf values + fn value_capacity(&self) -> usize { + self.values.capacity() + } } /// Represents the outcome of processing a node during leaf insertion @@ -4998,9 +5037,12 @@ mod tests { state.keys().copied(), ); + // Extract account nodes before moving hash_builder_updates + let hash_builder_account_nodes = hash_builder_updates.account_nodes.clone(); + // Write trie updates to the database let provider_rw = provider_factory.provider_rw().unwrap(); - provider_rw.write_trie_updates(&hash_builder_updates).unwrap(); + provider_rw.write_trie_updates(hash_builder_updates).unwrap(); provider_rw.commit().unwrap(); // Assert that the sparse trie root matches the hash builder root @@ -5008,7 +5050,7 @@ mod tests { // Assert that the sparse trie updates match the hash builder updates pretty_assertions::assert_eq!( BTreeMap::from_iter(sparse_updates.updated_nodes), - BTreeMap::from_iter(hash_builder_updates.account_nodes) + BTreeMap::from_iter(hash_builder_account_nodes) ); // Assert that the sparse trie nodes match the hash builder proof nodes assert_eq_parallel_sparse_trie_proof_nodes( @@ -5043,9 +5085,12 @@ mod tests { state.keys().copied(), ); + // Extract account nodes before moving hash_builder_updates + let hash_builder_account_nodes = hash_builder_updates.account_nodes.clone(); + // Write trie updates to the database let provider_rw = provider_factory.provider_rw().unwrap(); - provider_rw.write_trie_updates(&hash_builder_updates).unwrap(); + provider_rw.write_trie_updates(hash_builder_updates).unwrap(); provider_rw.commit().unwrap(); // Assert that the sparse trie root matches the hash builder root @@ -5053,7 +5098,7 @@ mod tests { // Assert that the sparse trie updates match the hash builder updates pretty_assertions::assert_eq!( BTreeMap::from_iter(sparse_updates.updated_nodes), - BTreeMap::from_iter(hash_builder_updates.account_nodes) + BTreeMap::from_iter(hash_builder_account_nodes) ); // Assert that the sparse trie nodes match the hash builder proof nodes assert_eq_parallel_sparse_trie_proof_nodes( diff --git a/crates/trie/sparse/Cargo.toml b/crates/trie/sparse/Cargo.toml index 6fac7c5faad..b2c7ee0f566 100644 --- a/crates/trie/sparse/Cargo.toml +++ b/crates/trie/sparse/Cargo.toml @@ -16,7 +16,7 @@ workspace = true reth-primitives-traits.workspace = true reth-execution-errors.workspace = true reth-trie-common.workspace = true -tracing.workspace = true +tracing = { workspace = true, features = ["attributes"] } alloy-trie.workspace = true # alloy diff --git a/crates/trie/sparse/src/metrics.rs b/crates/trie/sparse/src/metrics.rs index 430a831a2f7..3f39e6df6f9 100644 --- a/crates/trie/sparse/src/metrics.rs +++ b/crates/trie/sparse/src/metrics.rs @@ -1,5 +1,6 @@ //! Metrics for the sparse state trie +use metrics::Gauge; use reth_metrics::{metrics::Histogram, Metrics}; /// Metrics for the sparse state trie @@ -15,24 +16,24 @@ pub(crate) struct SparseStateTrieMetrics { pub(crate) multiproof_skipped_storage_nodes: u64, /// Number of total storage nodes, including those that were skipped. pub(crate) multiproof_total_storage_nodes: u64, - /// The actual metrics we will record into the histogram - pub(crate) histograms: SparseStateTrieHistograms, + /// The actual metrics we will record + pub(crate) inner_metrics: SparseStateTrieInnerMetrics, } impl SparseStateTrieMetrics { /// Record the metrics into the histograms pub(crate) fn record(&mut self) { use core::mem::take; - self.histograms + self.inner_metrics .multiproof_skipped_account_nodes .record(take(&mut self.multiproof_skipped_account_nodes) as f64); - self.histograms + self.inner_metrics .multiproof_total_account_nodes .record(take(&mut self.multiproof_total_account_nodes) as f64); - self.histograms + self.inner_metrics .multiproof_skipped_storage_nodes .record(take(&mut self.multiproof_skipped_storage_nodes) as f64); - self.histograms + self.inner_metrics .multiproof_total_storage_nodes .record(take(&mut self.multiproof_total_storage_nodes) as f64); } @@ -56,12 +57,28 @@ impl SparseStateTrieMetrics { pub(crate) const fn increment_total_storage_nodes(&mut self, count: u64) { self.multiproof_total_storage_nodes += count; } + + /// Set the value capacity for the sparse state trie + pub(crate) fn set_value_capacity(&self, capacity: usize) { + self.inner_metrics.value_capacity.set(capacity as f64); + } + + /// Set the node capacity for the sparse state trie + pub(crate) fn set_node_capacity(&self, capacity: usize) { + self.inner_metrics.node_capacity.set(capacity as f64); + } + + /// Set the number of cleared and active storage tries + pub(crate) fn set_storage_trie_metrics(&self, cleared: usize, active: usize) { + self.inner_metrics.cleared_storage_tries.set(cleared as f64); + self.inner_metrics.active_storage_tries.set(active as f64); + } } /// Metrics for the sparse state trie #[derive(Metrics)] #[metrics(scope = "sparse_state_trie")] -pub(crate) struct SparseStateTrieHistograms { +pub(crate) struct SparseStateTrieInnerMetrics { /// Histogram of account nodes that were skipped during a multiproof reveal due to being /// redundant (i.e. they were already revealed) pub(crate) multiproof_skipped_account_nodes: Histogram, @@ -72,4 +89,12 @@ pub(crate) struct SparseStateTrieHistograms { pub(crate) multiproof_skipped_storage_nodes: Histogram, /// Histogram of total storage nodes, including those that were skipped. pub(crate) multiproof_total_storage_nodes: Histogram, + /// Gauge for the trie's node capacity + pub(crate) node_capacity: Gauge, + /// Gauge for the trie's value capacity + pub(crate) value_capacity: Gauge, + /// The current number of cleared storage tries. + pub(crate) cleared_storage_tries: Gauge, + /// The number of currently active storage tries, i.e., not cleared + pub(crate) active_storage_tries: Gauge, } diff --git a/crates/trie/sparse/src/state.rs b/crates/trie/sparse/src/state.rs index fde4810da57..aef552da3dd 100644 --- a/crates/trie/sparse/src/state.rs +++ b/crates/trie/sparse/src/state.rs @@ -18,7 +18,7 @@ use reth_trie_common::{ DecodedMultiProof, DecodedStorageMultiProof, MultiProof, Nibbles, RlpNode, StorageMultiProof, TrieAccount, TrieMask, TrieNode, EMPTY_ROOT_HASH, TRIE_ACCOUNT_RLP_MAX_SIZE, }; -use tracing::trace; +use tracing::{instrument, trace}; /// Provides type-safe re-use of cleared [`SparseStateTrie`]s, which helps to save allocations /// across payload runs. @@ -208,6 +208,14 @@ where /// Reveal unknown trie paths from decoded multiproof. /// NOTE: This method does not extensively validate the proof. + #[instrument( + target = "trie::sparse", + skip_all, + fields( + account_nodes = multiproof.account_subtree.len(), + storages = multiproof.storages.len() + ) + )] pub fn reveal_decoded_multiproof( &mut self, multiproof: DecodedMultiProof, @@ -532,6 +540,7 @@ where /// Calculates the hashes of subtries. /// /// If the trie has not been revealed, this function does nothing. + #[instrument(target = "trie::sparse", skip_all)] pub fn calculate_subtries(&mut self) { if let SparseTrie::Revealed(trie) = &mut self.state { trie.update_subtrie_hashes(); @@ -576,21 +585,38 @@ where &mut self, provider_factory: impl TrieNodeProviderFactory, ) -> SparseStateTrieResult { - // record revealed node metrics + // record revealed node metrics and capacity metrics #[cfg(feature = "metrics")] - self.metrics.record(); + { + self.metrics.record(); + self.metrics.set_node_capacity(self.node_capacity()); + self.metrics.set_value_capacity(self.value_capacity()); + self.metrics.set_storage_trie_metrics( + self.storage.cleared_tries.len(), + self.storage.tries.len(), + ); + } Ok(self.revealed_trie_mut(provider_factory)?.root()) } /// Returns sparse trie root and trie updates if the trie has been revealed. + #[instrument(target = "trie::sparse", skip_all)] pub fn root_with_updates( &mut self, provider_factory: impl TrieNodeProviderFactory, ) -> SparseStateTrieResult<(B256, TrieUpdates)> { - // record revealed node metrics + // record revealed node metrics and capacity metrics #[cfg(feature = "metrics")] - self.metrics.record(); + { + self.metrics.record(); + self.metrics.set_node_capacity(self.node_capacity()); + self.metrics.set_value_capacity(self.value_capacity()); + self.metrics.set_storage_trie_metrics( + self.storage.cleared_tries.len(), + self.storage.tries.len(), + ); + } let storage_tries = self.storage_trie_updates(); let revealed = self.revealed_trie_mut(provider_factory)?; @@ -679,6 +705,7 @@ where /// /// Returns false if the new account info and storage trie are empty, indicating the account /// leaf should be removed. + #[instrument(target = "trie::sparse", skip_all)] pub fn update_account( &mut self, address: B256, @@ -721,6 +748,7 @@ where /// /// Returns false if the new storage root is empty, and the account info was already empty, /// indicating the account leaf should be removed. + #[instrument(target = "trie::sparse", skip_all)] pub fn update_account_storage_root( &mut self, address: B256, @@ -768,6 +796,7 @@ where } /// Remove the account leaf node. + #[instrument(target = "trie::sparse", skip_all)] pub fn remove_account_leaf( &mut self, path: &Nibbles, @@ -792,6 +821,16 @@ where storage_trie.remove_leaf(slot, provider)?; Ok(()) } + + /// The sum of the account trie's node capacity and the storage tries' node capacity + pub fn node_capacity(&self) -> usize { + self.state.node_capacity() + self.storage.total_node_capacity() + } + + /// The sum of the account trie's value capacity and the storage tries' value capacity + pub fn value_capacity(&self) -> usize { + self.state.value_capacity() + self.storage.total_value_capacity() + } } /// The fields of [`SparseStateTrie`] related to storage tries. This is kept separate from the rest @@ -867,6 +906,46 @@ impl StorageTries { .remove(account) .unwrap_or_else(|| self.cleared_revealed_paths.pop().unwrap_or_default()) } + + /// Sums the total node capacity in `cleared_tries` + fn total_cleared_tries_node_capacity(&self) -> usize { + self.cleared_tries.iter().map(|trie| trie.node_capacity()).sum() + } + + /// Sums the total value capacity in `cleared_tries` + fn total_cleared_tries_value_capacity(&self) -> usize { + self.cleared_tries.iter().map(|trie| trie.value_capacity()).sum() + } + + /// Calculates the sum of the active storage trie node capacity, ie the tries in `tries` + fn total_active_tries_node_capacity(&self) -> usize { + self.tries.values().map(|trie| trie.node_capacity()).sum() + } + + /// Calculates the sum of the active storage trie value capacity, ie the tries in `tries` + fn total_active_tries_value_capacity(&self) -> usize { + self.tries.values().map(|trie| trie.value_capacity()).sum() + } + + /// Calculates the sum of active and cleared storage trie node capacity, i.e. the sum of + /// * [`StorageTries::total_active_tries_node_capacity`], and + /// * [`StorageTries::total_cleared_tries_node_capacity`] + /// * the default trie's node capacity + fn total_node_capacity(&self) -> usize { + self.total_active_tries_node_capacity() + + self.total_cleared_tries_node_capacity() + + self.default_trie.node_capacity() + } + + /// Calculates the sum of active and cleared storage trie value capacity, i.e. the sum of + /// * [`StorageTries::total_active_tries_value_capacity`], and + /// * [`StorageTries::total_cleared_tries_value_capacity`], and + /// * the default trie's value capacity + fn total_value_capacity(&self) -> usize { + self.total_active_tries_value_capacity() + + self.total_cleared_tries_value_capacity() + + self.default_trie.value_capacity() + } } #[derive(Debug, PartialEq, Eq, Default)] diff --git a/crates/trie/sparse/src/traits.rs b/crates/trie/sparse/src/traits.rs index 300ac39c1b6..8fdbb78d876 100644 --- a/crates/trie/sparse/src/traits.rs +++ b/crates/trie/sparse/src/traits.rs @@ -222,6 +222,12 @@ pub trait SparseTrieInterface: Sized + Debug + Send + Sync { /// /// This is useful for reusing the trie without needing to reallocate memory. fn clear(&mut self); + + /// This returns the capacity of any inner data structures which store nodes. + fn node_capacity(&self) -> usize; + + /// This returns the capacity of any inner data structures which store leaf values. + fn value_capacity(&self) -> usize; } /// Struct for passing around branch node mask information. diff --git a/crates/trie/sparse/src/trie.rs b/crates/trie/sparse/src/trie.rs index 89a23851e28..737da842254 100644 --- a/crates/trie/sparse/src/trie.rs +++ b/crates/trie/sparse/src/trie.rs @@ -24,7 +24,7 @@ use reth_trie_common::{ TrieNode, CHILD_INDEX_RANGE, EMPTY_ROOT_HASH, }; use smallvec::SmallVec; -use tracing::{debug, trace}; +use tracing::{debug, instrument, trace}; /// The level below which the sparse trie hashes are calculated in /// [`SerialSparseTrie::update_subtrie_hashes`]. @@ -175,6 +175,7 @@ impl SparseTrie { /// and resetting the trie to only contain an empty root node. /// /// Note: This method will error if the trie is blinded. + #[instrument(target = "trie::sparse", skip_all)] pub fn wipe(&mut self) -> SparseTrieResult<()> { let revealed = self.as_revealed_mut().ok_or(SparseTrieErrorKind::Blind)?; revealed.wipe(); @@ -191,6 +192,7 @@ impl SparseTrie { /// /// - `Some(B256)` with the calculated root hash if the trie is revealed. /// - `None` if the trie is still blind. + #[instrument(target = "trie::sparse", skip_all)] pub fn root(&mut self) -> Option { Some(self.as_revealed_mut()?.root()) } @@ -230,6 +232,7 @@ impl SparseTrie { /// # Errors /// /// Returns an error if the trie is still blind, or if the update fails. + #[instrument(target = "trie::sparse", skip_all)] pub fn update_leaf( &mut self, path: Nibbles, @@ -246,6 +249,7 @@ impl SparseTrie { /// # Errors /// /// Returns an error if the trie is still blind, or if the leaf cannot be removed + #[instrument(target = "trie::sparse", skip_all)] pub fn remove_leaf( &mut self, path: &Nibbles, @@ -255,6 +259,22 @@ impl SparseTrie { revealed.remove_leaf(path, provider)?; Ok(()) } + + /// Returns the allocated capacity for sparse trie nodes. + pub fn node_capacity(&self) -> usize { + match self { + Self::Blind(Some(trie)) | Self::Revealed(trie) => trie.node_capacity(), + _ => 0, + } + } + + /// Returns the allocated capacity for sparse trie values. + pub fn value_capacity(&self) -> usize { + match self { + Self::Blind(Some(trie)) | Self::Revealed(trie) => trie.value_capacity(), + _ => 0, + } + } } /// The representation of revealed sparse trie. @@ -573,14 +593,13 @@ impl SparseTrieInterface for SerialSparseTrie { Ok(()) } + #[instrument(target = "trie::sparse::serial", skip(self, provider))] fn update_leaf( &mut self, full_path: Nibbles, value: Vec, provider: P, ) -> SparseTrieResult<()> { - trace!(target: "trie::sparse", ?full_path, ?value, "update_leaf called"); - self.prefix_set.insert(full_path); let existing = self.values.insert(full_path, value); if existing.is_some() { @@ -712,6 +731,7 @@ impl SparseTrieInterface for SerialSparseTrie { Ok(()) } + #[instrument(target = "trie::sparse::serial", skip(self, provider))] fn remove_leaf( &mut self, full_path: &Nibbles, @@ -897,6 +917,7 @@ impl SparseTrieInterface for SerialSparseTrie { Ok(()) } + #[instrument(target = "trie::sparse::serial", skip(self))] fn root(&mut self) -> B256 { // Take the current prefix set let mut prefix_set = core::mem::take(&mut self.prefix_set).freeze(); @@ -1059,6 +1080,14 @@ impl SparseTrieInterface for SerialSparseTrie { // If we get here, there's no leaf at the target path Ok(LeafLookup::NonExistent) } + + fn node_capacity(&self) -> usize { + self.nodes.capacity() + } + + fn value_capacity(&self) -> usize { + self.values.capacity() + } } impl SerialSparseTrie { @@ -1324,6 +1353,7 @@ impl SerialSparseTrie { /// /// This function identifies all nodes that have changed (based on the prefix set) at the given /// depth and recalculates their RLP representation. + #[instrument(target = "trie::sparse::serial", skip(self))] pub fn update_rlp_node_level(&mut self, depth: usize) { // Take the current prefix set let mut prefix_set = core::mem::take(&mut self.prefix_set).freeze(); @@ -1369,6 +1399,7 @@ impl SerialSparseTrie { /// specified depth. /// - A `PrefixSetMut` containing paths shallower than the specified depth that still need to be /// tracked for future updates. + #[instrument(target = "trie::sparse::serial", skip(self))] fn get_changed_nodes_at_depth( &self, prefix_set: &mut PrefixSet, @@ -1455,6 +1486,7 @@ impl SerialSparseTrie { /// # Panics /// /// If the node at provided path does not exist. + #[instrument(target = "trie::sparse::serial", skip_all, ret(level = "trace"))] pub fn rlp_node( &mut self, prefix_set: &mut PrefixSet, @@ -3034,9 +3066,12 @@ mod tests { state.keys().copied(), ); + // Extract account nodes before moving hash_builder_updates + let hash_builder_account_nodes = hash_builder_updates.account_nodes.clone(); + // Write trie updates to the database let provider_rw = provider_factory.provider_rw().unwrap(); - provider_rw.write_trie_updates(&hash_builder_updates).unwrap(); + provider_rw.write_trie_updates(hash_builder_updates).unwrap(); provider_rw.commit().unwrap(); // Assert that the sparse trie root matches the hash builder root @@ -3044,7 +3079,7 @@ mod tests { // Assert that the sparse trie updates match the hash builder updates pretty_assertions::assert_eq!( BTreeMap::from_iter(sparse_updates.updated_nodes), - BTreeMap::from_iter(hash_builder_updates.account_nodes) + BTreeMap::from_iter(hash_builder_account_nodes) ); // Assert that the sparse trie nodes match the hash builder proof nodes assert_eq_sparse_trie_proof_nodes(&updated_sparse, hash_builder_proof_nodes); @@ -3076,9 +3111,12 @@ mod tests { state.keys().copied(), ); + // Extract account nodes before moving hash_builder_updates + let hash_builder_account_nodes = hash_builder_updates.account_nodes.clone(); + // Write trie updates to the database let provider_rw = provider_factory.provider_rw().unwrap(); - provider_rw.write_trie_updates(&hash_builder_updates).unwrap(); + provider_rw.write_trie_updates(hash_builder_updates).unwrap(); provider_rw.commit().unwrap(); // Assert that the sparse trie root matches the hash builder root @@ -3086,7 +3124,7 @@ mod tests { // Assert that the sparse trie updates match the hash builder updates pretty_assertions::assert_eq!( BTreeMap::from_iter(sparse_updates.updated_nodes), - BTreeMap::from_iter(hash_builder_updates.account_nodes) + BTreeMap::from_iter(hash_builder_account_nodes) ); // Assert that the sparse trie nodes match the hash builder proof nodes assert_eq_sparse_trie_proof_nodes(&updated_sparse, hash_builder_proof_nodes); diff --git a/crates/trie/trie/src/hashed_cursor/mock.rs b/crates/trie/trie/src/hashed_cursor/mock.rs index 895bf852a22..308f05e4c8a 100644 --- a/crates/trie/trie/src/hashed_cursor/mock.rs +++ b/crates/trie/trie/src/hashed_cursor/mock.rs @@ -101,7 +101,7 @@ impl MockHashedCursor { impl HashedCursor for MockHashedCursor { type Value = T; - #[instrument(level = "trace", skip(self), ret)] + #[instrument(skip(self), ret(level = "trace"))] fn seek(&mut self, key: B256) -> Result, DatabaseError> { // Find the first key that is greater than or equal to the given key. let entry = self.values.iter().find_map(|(k, v)| (k >= &key).then(|| (*k, v.clone()))); @@ -115,7 +115,7 @@ impl HashedCursor for MockHashedCursor { Ok(entry) } - #[instrument(level = "trace", skip(self), ret)] + #[instrument(skip(self), ret(level = "trace"))] fn next(&mut self) -> Result, DatabaseError> { let mut iter = self.values.iter(); // Jump to the first key that has a prefix of the current key if it's set, or to the first diff --git a/crates/trie/trie/src/node_iter.rs b/crates/trie/trie/src/node_iter.rs index 862176c803a..e11cd51f790 100644 --- a/crates/trie/trie/src/node_iter.rs +++ b/crates/trie/trie/src/node_iter.rs @@ -191,11 +191,10 @@ where /// /// NOTE: The iteration will start from the key of the previous hashed entry if it was supplied. #[instrument( - level = "trace", target = "trie::node_iter", skip_all, fields(trie_type = ?self.trie_type), - ret + ret(level = "trace") )] pub fn try_next( &mut self, diff --git a/crates/trie/trie/src/trie_cursor/mock.rs b/crates/trie/trie/src/trie_cursor/mock.rs index 4b0b7f699dc..add2d7ddef3 100644 --- a/crates/trie/trie/src/trie_cursor/mock.rs +++ b/crates/trie/trie/src/trie_cursor/mock.rs @@ -103,7 +103,7 @@ impl MockTrieCursor { } impl TrieCursor for MockTrieCursor { - #[instrument(level = "trace", skip(self), ret)] + #[instrument(skip(self), ret(level = "trace"))] fn seek_exact( &mut self, key: Nibbles, @@ -119,7 +119,7 @@ impl TrieCursor for MockTrieCursor { Ok(entry) } - #[instrument(level = "trace", skip(self), ret)] + #[instrument(skip(self), ret(level = "trace"))] fn seek( &mut self, key: Nibbles, @@ -136,7 +136,7 @@ impl TrieCursor for MockTrieCursor { Ok(entry) } - #[instrument(level = "trace", skip(self), ret)] + #[instrument(skip(self), ret(level = "trace"))] fn next(&mut self) -> Result, DatabaseError> { let mut iter = self.trie_nodes.iter(); // Jump to the first key that has a prefix of the current key if it's set, or to the first @@ -155,7 +155,7 @@ impl TrieCursor for MockTrieCursor { Ok(entry) } - #[instrument(level = "trace", skip(self), ret)] + #[instrument(skip(self), ret(level = "trace"))] fn current(&mut self) -> Result, DatabaseError> { Ok(self.current_key) } diff --git a/crates/trie/trie/src/trie_cursor/mod.rs b/crates/trie/trie/src/trie_cursor/mod.rs index 01eea4c40e6..269611150d6 100644 --- a/crates/trie/trie/src/trie_cursor/mod.rs +++ b/crates/trie/trie/src/trie_cursor/mod.rs @@ -58,3 +58,48 @@ pub trait TrieCursor: Send + Sync { /// Get the current entry. fn current(&mut self) -> Result, DatabaseError>; } + +/// Iterator wrapper for `TrieCursor` types +#[derive(Debug)] +pub struct TrieCursorIter<'a, C> { + cursor: &'a mut C, + /// The initial value from seek, if any + initial: Option>, +} + +impl<'a, C> TrieCursorIter<'a, C> { + /// Create a new iterator from a mutable reference to a cursor. The Iterator will start from the + /// empty path. + pub fn new(cursor: &'a mut C) -> Self + where + C: TrieCursor, + { + let initial = cursor.seek(Nibbles::default()).transpose(); + Self { cursor, initial } + } +} + +impl<'a, C> From<&'a mut C> for TrieCursorIter<'a, C> +where + C: TrieCursor, +{ + fn from(cursor: &'a mut C) -> Self { + Self::new(cursor) + } +} + +impl<'a, C> Iterator for TrieCursorIter<'a, C> +where + C: TrieCursor, +{ + type Item = Result<(Nibbles, BranchNodeCompact), DatabaseError>; + + fn next(&mut self) -> Option { + // If we have an initial value from seek, return it first + if let Some(initial) = self.initial.take() { + return Some(initial); + } + + self.cursor.next().transpose() + } +} diff --git a/crates/trie/trie/src/walker.rs b/crates/trie/trie/src/walker.rs index f12bf46f748..0ea466437f5 100644 --- a/crates/trie/trie/src/walker.rs +++ b/crates/trie/trie/src/walker.rs @@ -157,7 +157,7 @@ impl> TrieWalker { } /// Returns the next unprocessed key in the trie along with its raw [`Nibbles`] representation. - #[instrument(level = "trace", skip(self), ret)] + #[instrument(skip(self), ret(level = "trace"))] pub fn next_unprocessed_key(&self) -> Option<(B256, Nibbles)> { self.key() .and_then(|key| if self.can_skip_current_node { key.increment() } else { Some(*key) }) @@ -297,7 +297,7 @@ impl> TrieWalker { } /// Consumes the next node in the trie, updating the stack. - #[instrument(level = "trace", skip(self), ret)] + #[instrument(skip(self), ret(level = "trace"))] fn consume_node(&mut self) -> Result<(), DatabaseError> { let Some((key, node)) = self.node(false)? else { // If no next node is found, clear the stack. @@ -343,7 +343,7 @@ impl> TrieWalker { } /// Moves to the next sibling node in the trie, updating the stack. - #[instrument(level = "trace", skip(self), ret)] + #[instrument(skip(self), ret(level = "trace"))] fn move_to_next_sibling( &mut self, allow_root_to_child_nibble: bool, diff --git a/docs/vocs/docs/pages/cli/reth.mdx b/docs/vocs/docs/pages/cli/reth.mdx index 5f0ccfca01f..0344c23bf2c 100644 --- a/docs/vocs/docs/pages/cli/reth.mdx +++ b/docs/vocs/docs/pages/cli/reth.mdx @@ -96,7 +96,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -116,16 +116,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/config.mdx b/docs/vocs/docs/pages/cli/reth/config.mdx index 849f4ec5bab..adc08cd96e6 100644 --- a/docs/vocs/docs/pages/cli/reth/config.mdx +++ b/docs/vocs/docs/pages/cli/reth/config.mdx @@ -82,7 +82,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -102,16 +102,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db.mdx b/docs/vocs/docs/pages/cli/reth/db.mdx index 3b28b43162a..91397e0f7e9 100644 --- a/docs/vocs/docs/pages/cli/reth/db.mdx +++ b/docs/vocs/docs/pages/cli/reth/db.mdx @@ -147,7 +147,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -167,16 +167,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/checksum.mdx b/docs/vocs/docs/pages/cli/reth/db/checksum.mdx index 13e2c2bd39d..834fd42e447 100644 --- a/docs/vocs/docs/pages/cli/reth/db/checksum.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/checksum.mdx @@ -99,7 +99,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -119,16 +119,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/clear.mdx b/docs/vocs/docs/pages/cli/reth/db/clear.mdx index 5c19682e8b6..0b64cefb71b 100644 --- a/docs/vocs/docs/pages/cli/reth/db/clear.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/clear.mdx @@ -91,7 +91,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -111,16 +111,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/clear/mdbx.mdx b/docs/vocs/docs/pages/cli/reth/db/clear/mdbx.mdx index 0e5526affe5..eb4120a34cb 100644 --- a/docs/vocs/docs/pages/cli/reth/db/clear/mdbx.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/clear/mdbx.mdx @@ -90,7 +90,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -110,16 +110,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/clear/static-file.mdx b/docs/vocs/docs/pages/cli/reth/db/clear/static-file.mdx index 72c3108fcf3..913c6fcc5eb 100644 --- a/docs/vocs/docs/pages/cli/reth/db/clear/static-file.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/clear/static-file.mdx @@ -93,7 +93,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -113,16 +113,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/diff.mdx b/docs/vocs/docs/pages/cli/reth/db/diff.mdx index fadd0613ca8..b5120d7409a 100644 --- a/docs/vocs/docs/pages/cli/reth/db/diff.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/diff.mdx @@ -126,7 +126,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -146,16 +146,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/drop.mdx b/docs/vocs/docs/pages/cli/reth/db/drop.mdx index 0f9ddba9ee9..e0a54dcac35 100644 --- a/docs/vocs/docs/pages/cli/reth/db/drop.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/drop.mdx @@ -89,7 +89,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -109,16 +109,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/get.mdx b/docs/vocs/docs/pages/cli/reth/db/get.mdx index 942eda79998..0d027754d59 100644 --- a/docs/vocs/docs/pages/cli/reth/db/get.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/get.mdx @@ -91,7 +91,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -111,16 +111,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/get/mdbx.mdx b/docs/vocs/docs/pages/cli/reth/db/get/mdbx.mdx index b7ccf9e7d3d..2ea1ea48f2e 100644 --- a/docs/vocs/docs/pages/cli/reth/db/get/mdbx.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/get/mdbx.mdx @@ -99,7 +99,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -119,16 +119,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/get/static-file.mdx b/docs/vocs/docs/pages/cli/reth/db/get/static-file.mdx index 28d7c343e94..21e08493453 100644 --- a/docs/vocs/docs/pages/cli/reth/db/get/static-file.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/get/static-file.mdx @@ -99,7 +99,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -119,16 +119,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/list.mdx b/docs/vocs/docs/pages/cli/reth/db/list.mdx index 3f9ac94c5c5..55e14d822cd 100644 --- a/docs/vocs/docs/pages/cli/reth/db/list.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/list.mdx @@ -132,7 +132,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -152,16 +152,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/path.mdx b/docs/vocs/docs/pages/cli/reth/db/path.mdx index f6714898b35..3f95c5761d9 100644 --- a/docs/vocs/docs/pages/cli/reth/db/path.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/path.mdx @@ -86,7 +86,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -106,16 +106,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/repair-trie.mdx b/docs/vocs/docs/pages/cli/reth/db/repair-trie.mdx index 3a6bfae1d3c..d972bcccd54 100644 --- a/docs/vocs/docs/pages/cli/reth/db/repair-trie.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/repair-trie.mdx @@ -89,7 +89,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -109,16 +109,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/stats.mdx b/docs/vocs/docs/pages/cli/reth/db/stats.mdx index a4939c3ef93..1fd305c4e63 100644 --- a/docs/vocs/docs/pages/cli/reth/db/stats.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/stats.mdx @@ -99,7 +99,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -119,16 +119,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/db/version.mdx b/docs/vocs/docs/pages/cli/reth/db/version.mdx index 7b3766b4e8a..c2b50b8944f 100644 --- a/docs/vocs/docs/pages/cli/reth/db/version.mdx +++ b/docs/vocs/docs/pages/cli/reth/db/version.mdx @@ -86,7 +86,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -106,16 +106,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/download.mdx b/docs/vocs/docs/pages/cli/reth/download.mdx index 74296538855..1890b95821d 100644 --- a/docs/vocs/docs/pages/cli/reth/download.mdx +++ b/docs/vocs/docs/pages/cli/reth/download.mdx @@ -144,7 +144,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -164,16 +164,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/dump-genesis.mdx b/docs/vocs/docs/pages/cli/reth/dump-genesis.mdx index a6dbbcb1b27..4791d561980 100644 --- a/docs/vocs/docs/pages/cli/reth/dump-genesis.mdx +++ b/docs/vocs/docs/pages/cli/reth/dump-genesis.mdx @@ -85,7 +85,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -105,16 +105,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/export-era.mdx b/docs/vocs/docs/pages/cli/reth/export-era.mdx index ee65abbeb42..430e0948a99 100644 --- a/docs/vocs/docs/pages/cli/reth/export-era.mdx +++ b/docs/vocs/docs/pages/cli/reth/export-era.mdx @@ -150,7 +150,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -170,16 +170,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/import-era.mdx b/docs/vocs/docs/pages/cli/reth/import-era.mdx index ae17ab91e0e..c0d03852de9 100644 --- a/docs/vocs/docs/pages/cli/reth/import-era.mdx +++ b/docs/vocs/docs/pages/cli/reth/import-era.mdx @@ -145,7 +145,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -165,16 +165,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/import.mdx b/docs/vocs/docs/pages/cli/reth/import.mdx index f92b52ec591..b5795a6e1d7 100644 --- a/docs/vocs/docs/pages/cli/reth/import.mdx +++ b/docs/vocs/docs/pages/cli/reth/import.mdx @@ -146,7 +146,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -166,16 +166,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/init-state.mdx b/docs/vocs/docs/pages/cli/reth/init-state.mdx index 03d1e7b883b..1ba1affc519 100644 --- a/docs/vocs/docs/pages/cli/reth/init-state.mdx +++ b/docs/vocs/docs/pages/cli/reth/init-state.mdx @@ -80,9 +80,6 @@ Database: --header Header file containing the header in an RLP encoded format. - --total-difficulty - Total difficulty of the header. - --header-hash Hash of the header. @@ -169,7 +166,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -189,16 +186,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] + + --tracing-otlp.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. - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + Example: --tracing-otlp.filter=info,reth=debug,hyper_util=off Defaults to TRACE if not specified. diff --git a/docs/vocs/docs/pages/cli/reth/init.mdx b/docs/vocs/docs/pages/cli/reth/init.mdx index 993ae2dcd85..11777b1f6e6 100644 --- a/docs/vocs/docs/pages/cli/reth/init.mdx +++ b/docs/vocs/docs/pages/cli/reth/init.mdx @@ -134,7 +134,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -154,16 +154,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/node.mdx b/docs/vocs/docs/pages/cli/reth/node.mdx index a172256058b..a752f76b019 100644 --- a/docs/vocs/docs/pages/cli/reth/node.mdx +++ b/docs/vocs/docs/pages/cli/reth/node.mdx @@ -734,6 +734,11 @@ Dev testnet: Parses strings using [`humantime::parse_duration`] --dev.block-time 12s + --dev.mnemonic + Derive dev accounts from a fixed mnemonic instead of random ones. + + [default: "test test test test test test test test test test test junk"] + Pruning: --full Run full node. Only the most recent [`MINIMUM_PRUNING_DISTANCE`] block states are stored @@ -815,8 +820,8 @@ Engine: --engine.legacy-state-root Enable legacy state root - --engine.disable-caching-and-prewarming - Disable cross-block caching and parallel prewarming + --engine.disable-prewarming + Disable parallel prewarming --engine.disable-parallel-sparse-trie Disable the parallel sparse trie in the engine @@ -976,7 +981,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -996,16 +1001,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] + + --tracing-otlp.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. - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + Example: --tracing-otlp.filter=info,reth=debug,hyper_util=off Defaults to TRACE if not specified. diff --git a/docs/vocs/docs/pages/cli/reth/p2p.mdx b/docs/vocs/docs/pages/cli/reth/p2p.mdx index 9693e20e756..4138656604d 100644 --- a/docs/vocs/docs/pages/cli/reth/p2p.mdx +++ b/docs/vocs/docs/pages/cli/reth/p2p.mdx @@ -83,7 +83,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -103,16 +103,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/p2p/body.mdx b/docs/vocs/docs/pages/cli/reth/p2p/body.mdx index ae0f3d293d1..63f77913f9c 100644 --- a/docs/vocs/docs/pages/cli/reth/p2p/body.mdx +++ b/docs/vocs/docs/pages/cli/reth/p2p/body.mdx @@ -303,7 +303,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -323,16 +323,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/p2p/bootnode.mdx b/docs/vocs/docs/pages/cli/reth/p2p/bootnode.mdx index d1bf7c69870..578932411f6 100644 --- a/docs/vocs/docs/pages/cli/reth/p2p/bootnode.mdx +++ b/docs/vocs/docs/pages/cli/reth/p2p/bootnode.mdx @@ -94,7 +94,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -114,16 +114,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/p2p/header.mdx b/docs/vocs/docs/pages/cli/reth/p2p/header.mdx index 9e542916d4c..f9b3276ced0 100644 --- a/docs/vocs/docs/pages/cli/reth/p2p/header.mdx +++ b/docs/vocs/docs/pages/cli/reth/p2p/header.mdx @@ -303,7 +303,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -323,16 +323,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/p2p/rlpx.mdx b/docs/vocs/docs/pages/cli/reth/p2p/rlpx.mdx index 75ab654964f..8bf19d3ecab 100644 --- a/docs/vocs/docs/pages/cli/reth/p2p/rlpx.mdx +++ b/docs/vocs/docs/pages/cli/reth/p2p/rlpx.mdx @@ -80,7 +80,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -100,16 +100,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/p2p/rlpx/ping.mdx b/docs/vocs/docs/pages/cli/reth/p2p/rlpx/ping.mdx index 7152b222fb4..de13e93b561 100644 --- a/docs/vocs/docs/pages/cli/reth/p2p/rlpx/ping.mdx +++ b/docs/vocs/docs/pages/cli/reth/p2p/rlpx/ping.mdx @@ -80,7 +80,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -100,16 +100,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/prune.mdx b/docs/vocs/docs/pages/cli/reth/prune.mdx index f54f6687805..bc5d0385697 100644 --- a/docs/vocs/docs/pages/cli/reth/prune.mdx +++ b/docs/vocs/docs/pages/cli/reth/prune.mdx @@ -134,7 +134,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -154,16 +154,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/re-execute.mdx b/docs/vocs/docs/pages/cli/reth/re-execute.mdx index 973ac79f29f..dc3bcbe4627 100644 --- a/docs/vocs/docs/pages/cli/reth/re-execute.mdx +++ b/docs/vocs/docs/pages/cli/reth/re-execute.mdx @@ -147,7 +147,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -167,16 +167,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage.mdx b/docs/vocs/docs/pages/cli/reth/stage.mdx index f382eb2081e..85f2559de4d 100644 --- a/docs/vocs/docs/pages/cli/reth/stage.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage.mdx @@ -83,7 +83,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -103,16 +103,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/drop.mdx b/docs/vocs/docs/pages/cli/reth/stage/drop.mdx index e2ba5751b52..923fd5ff955 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/drop.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/drop.mdx @@ -72,17 +72,18 @@ Database: Possible values: - - headers: The headers stage within the pipeline - - bodies: The bodies stage within the pipeline - - senders: The senders stage within the pipeline - - execution: The execution stage within the pipeline - - account-hashing: The account hashing stage within the pipeline - - storage-hashing: The storage hashing stage within the pipeline - - hashing: The account and storage hashing stages within the pipeline - - merkle: The merkle stage within the pipeline - - tx-lookup: The transaction lookup stage within the pipeline - - account-history: The account history stage within the pipeline - - storage-history: The storage history stage within the pipeline + - headers: The headers stage within the pipeline + - bodies: The bodies stage within the pipeline + - senders: The senders stage within the pipeline + - execution: The execution stage within the pipeline + - account-hashing: The account hashing stage within the pipeline + - storage-hashing: The storage hashing stage within the pipeline + - hashing: The account and storage hashing stages within the pipeline + - merkle: The merkle stage within the pipeline + - merkle-changesets: The merkle changesets stage within the pipeline + - tx-lookup: The transaction lookup stage within the pipeline + - account-history: The account history stage within the pipeline + - storage-history: The storage history stage within the pipeline Logging: --log.stdout.format @@ -148,7 +149,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -168,16 +169,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/dump.mdx b/docs/vocs/docs/pages/cli/reth/stage/dump.mdx index 01b4f61f29f..2466edcb966 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/dump.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/dump.mdx @@ -141,7 +141,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -161,16 +161,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/dump/account-hashing.mdx b/docs/vocs/docs/pages/cli/reth/stage/dump/account-hashing.mdx index 18f44ae13ed..c79571b31c3 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/dump/account-hashing.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/dump/account-hashing.mdx @@ -98,7 +98,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -118,16 +118,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/dump/execution.mdx b/docs/vocs/docs/pages/cli/reth/stage/dump/execution.mdx index de0f693ed57..c2480bae00f 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/dump/execution.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/dump/execution.mdx @@ -98,7 +98,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -118,16 +118,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/dump/merkle.mdx b/docs/vocs/docs/pages/cli/reth/stage/dump/merkle.mdx index aaff755796a..423771b183b 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/dump/merkle.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/dump/merkle.mdx @@ -98,7 +98,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -118,16 +118,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/dump/storage-hashing.mdx b/docs/vocs/docs/pages/cli/reth/stage/dump/storage-hashing.mdx index 2ff7b22b76b..211f4e59979 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/dump/storage-hashing.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/dump/storage-hashing.mdx @@ -98,7 +98,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -118,16 +118,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/run.mdx b/docs/vocs/docs/pages/cli/reth/stage/run.mdx index 2af69a053d6..9eae5963a17 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/run.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/run.mdx @@ -101,17 +101,18 @@ Database: The name of the stage to run Possible values: - - headers: The headers stage within the pipeline - - bodies: The bodies stage within the pipeline - - senders: The senders stage within the pipeline - - execution: The execution stage within the pipeline - - account-hashing: The account hashing stage within the pipeline - - storage-hashing: The storage hashing stage within the pipeline - - hashing: The account and storage hashing stages within the pipeline - - merkle: The merkle stage within the pipeline - - tx-lookup: The transaction lookup stage within the pipeline - - account-history: The account history stage within the pipeline - - storage-history: The storage history stage within the pipeline + - headers: The headers stage within the pipeline + - bodies: The bodies stage within the pipeline + - senders: The senders stage within the pipeline + - execution: The execution stage within the pipeline + - account-hashing: The account hashing stage within the pipeline + - storage-hashing: The storage hashing stage within the pipeline + - hashing: The account and storage hashing stages within the pipeline + - merkle: The merkle stage within the pipeline + - merkle-changesets: The merkle changesets stage within the pipeline + - tx-lookup: The transaction lookup stage within the pipeline + - account-history: The account history stage within the pipeline + - storage-history: The storage history stage within the pipeline Networking: -d, --disable-discovery @@ -369,7 +370,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -389,16 +390,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/unwind.mdx b/docs/vocs/docs/pages/cli/reth/stage/unwind.mdx index 977d949a9b7..ab5776e2e5b 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/unwind.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/unwind.mdx @@ -142,7 +142,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -162,16 +162,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/unwind/num-blocks.mdx b/docs/vocs/docs/pages/cli/reth/stage/unwind/num-blocks.mdx index 0b60467c413..500cb3197fb 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/unwind/num-blocks.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/unwind/num-blocks.mdx @@ -90,7 +90,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -110,16 +110,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/docs/vocs/docs/pages/cli/reth/stage/unwind/to-block.mdx b/docs/vocs/docs/pages/cli/reth/stage/unwind/to-block.mdx index 07632cf8285..4ec68dbb1ec 100644 --- a/docs/vocs/docs/pages/cli/reth/stage/unwind/to-block.mdx +++ b/docs/vocs/docs/pages/cli/reth/stage/unwind/to-block.mdx @@ -90,7 +90,7 @@ Logging: Possible values: - always: Colors on - - auto: Colors on + - auto: Auto-detect - never: Colors off [default: always] @@ -110,16 +110,18 @@ Display: Tracing: --tracing-otlp[=] - Enable `Opentelemetry` tracing export to an OTLP endpoint. + Enable `Opentelemetry` tracing export to an OTLP endpoint. Currently only http exporting is supported. If no value provided, defaults to `http://localhost:4318/v1/traces`. Example: --tracing-otlp=http://collector:4318/v1/traces - --tracing-otlp-level - Set the minimum log level for OTLP traces. + [env: OTEL_EXPORTER_OTLP_TRACES_ENDPOINT=] - Valid values: ERROR, WARN, INFO, DEBUG, TRACE + --tracing-otlp.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. diff --git a/examples/custom-beacon-withdrawals/src/main.rs b/examples/custom-beacon-withdrawals/src/main.rs index a72b2c44487..1d93226dd6a 100644 --- a/examples/custom-beacon-withdrawals/src/main.rs +++ b/examples/custom-beacon-withdrawals/src/main.rs @@ -8,7 +8,7 @@ use alloy_evm::{ block::{BlockExecutorFactory, BlockExecutorFor, ExecutableTx}, eth::{EthBlockExecutionCtx, EthBlockExecutor}, precompiles::PrecompilesMap, - revm::context::result::ResultAndState, + revm::context::{result::ResultAndState, Block as _}, EthEvm, EthEvmFactory, }; use alloy_sol_macro::sol; @@ -271,7 +271,7 @@ pub fn apply_withdrawals_contract_call( // Clean-up post system tx context state.remove(&SYSTEM_ADDRESS); - state.remove(&evm.block().beneficiary); + state.remove(&evm.block().beneficiary()); evm.db_mut().commit(state); diff --git a/examples/custom-evm/src/main.rs b/examples/custom-evm/src/main.rs index b5e69670ec7..e32f0be6bd5 100644 --- a/examples/custom-evm/src/main.rs +++ b/examples/custom-evm/src/main.rs @@ -18,7 +18,7 @@ use reth_ethereum::{ evm::{ primitives::{Database, EvmEnv}, revm::{ - context::{Context, TxEnv}, + context::{BlockEnv, Context, TxEnv}, context_interface::result::{EVMError, HaltReason}, inspector::{Inspector, NoOpInspector}, interpreter::interpreter::EthInterpreter, @@ -54,6 +54,7 @@ impl EvmFactory for MyEvmFactory { type HaltReason = HaltReason; type Context = EthEvmContext; type Spec = SpecId; + type BlockEnv = BlockEnv; type Precompiles = PrecompilesMap; fn create_evm(&self, db: DB, input: EvmEnv) -> Self::Evm { diff --git a/examples/custom-node/src/engine.rs b/examples/custom-node/src/engine.rs index 357290e14d7..0c80e52a661 100644 --- a/examples/custom-node/src/engine.rs +++ b/examples/custom-node/src/engine.rs @@ -6,7 +6,7 @@ use crate::{ }; use alloy_eips::eip2718::WithEncoded; use op_alloy_rpc_types_engine::{OpExecutionData, OpExecutionPayload}; -use reth_chain_state::ExecutedBlockWithTrieUpdates; +use reth_chain_state::ExecutedBlock; use reth_engine_primitives::EngineApiValidator; use reth_ethereum::{ node::api::{ @@ -167,7 +167,7 @@ impl BuiltPayload for CustomBuiltPayload { self.0.fees() } - fn executed_block(&self) -> Option> { + fn executed_block(&self) -> Option> { self.0.executed_block() } diff --git a/examples/custom-node/src/evm/alloy.rs b/examples/custom-node/src/evm/alloy.rs index 6071a2c6dd8..d8df842cfc5 100644 --- a/examples/custom-node/src/evm/alloy.rs +++ b/examples/custom-node/src/evm/alloy.rs @@ -40,6 +40,7 @@ where type Error = EVMError; type HaltReason = OpHaltReason; type Spec = OpSpecId; + type BlockEnv = BlockEnv; type Precompiles = P; type Inspector = I; @@ -103,6 +104,7 @@ impl EvmFactory for CustomEvmFactory { type Error = EVMError; type HaltReason = OpHaltReason; type Spec = OpSpecId; + type BlockEnv = BlockEnv; type Precompiles = PrecompilesMap; fn create_evm( diff --git a/examples/custom-node/src/evm/env.rs b/examples/custom-node/src/evm/env.rs index 5508ec4e6d0..53a2b4e3f15 100644 --- a/examples/custom-node/src/evm/env.rs +++ b/examples/custom-node/src/evm/env.rs @@ -1,6 +1,7 @@ use crate::primitives::{CustomTransaction, TxPayment}; use alloy_eips::{eip2930::AccessList, Typed2718}; use alloy_evm::{FromRecoveredTx, FromTxWithEncoded, IntoTxEnv}; +use alloy_op_evm::block::OpTxEnv; use alloy_primitives::{Address, Bytes, TxKind, B256, U256}; use op_alloy_consensus::OpTxEnvelope; use op_revm::OpTransaction; @@ -328,3 +329,12 @@ impl IntoTxEnv for CustomTxEnv { self } } + +impl OpTxEnv for CustomTxEnv { + fn encoded_bytes(&self) -> Option<&Bytes> { + match self { + Self::Op(tx) => tx.encoded_bytes(), + Self::Payment(_) => None, + } + } +} diff --git a/examples/custom-node/src/primitives/tx.rs b/examples/custom-node/src/primitives/tx.rs index f04bcc8862f..7c282922f48 100644 --- a/examples/custom-node/src/primitives/tx.rs +++ b/examples/custom-node/src/primitives/tx.rs @@ -33,7 +33,7 @@ impl RlpBincode for CustomTransaction {} impl reth_codecs::alloy::transaction::Envelope for CustomTransaction { fn signature(&self) -> &Signature { match self { - CustomTransaction::Op(tx) => tx.signature(), + CustomTransaction::Op(tx) => reth_codecs::alloy::transaction::Envelope::signature(tx), CustomTransaction::Payment(tx) => tx.signature(), } } diff --git a/examples/precompile-cache/src/main.rs b/examples/precompile-cache/src/main.rs index 69aaf7b4035..fe748db4636 100644 --- a/examples/precompile-cache/src/main.rs +++ b/examples/precompile-cache/src/main.rs @@ -16,7 +16,7 @@ use reth_ethereum::{ evm::{ primitives::{Database, EvmEnv}, revm::{ - context::{Context, TxEnv}, + context::{BlockEnv, Context, TxEnv}, context_interface::result::{EVMError, HaltReason}, inspector::{Inspector, NoOpInspector}, interpreter::interpreter::EthInterpreter, @@ -69,6 +69,7 @@ impl EvmFactory for MyEvmFactory { type HaltReason = HaltReason; type Context = EthEvmContext; type Spec = SpecId; + type BlockEnv = BlockEnv; type Precompiles = PrecompilesMap; fn create_evm(&self, db: DB, input: EvmEnv) -> Self::Evm { diff --git a/testing/ef-tests/Cargo.toml b/testing/ef-tests/Cargo.toml index 6b11e29c707..745172cd82c 100644 --- a/testing/ef-tests/Cargo.toml +++ b/testing/ef-tests/Cargo.toml @@ -28,7 +28,7 @@ reth-evm.workspace = true reth-evm-ethereum.workspace = true reth-ethereum-consensus.workspace = true reth-revm = { workspace = true, features = ["std", "witness"] } -reth-stateless = { workspace = true } +reth-stateless = { workspace = true, features = ["secp256k1"] } reth-tracing.workspace = true reth-trie.workspace = true reth-trie-db.workspace = true diff --git a/testing/ef-tests/src/cases/blockchain_test.rs b/testing/ef-tests/src/cases/blockchain_test.rs index 0526efaa6ef..c54ef2ad7b1 100644 --- a/testing/ef-tests/src/cases/blockchain_test.rs +++ b/testing/ef-tests/src/cases/blockchain_test.rs @@ -10,17 +10,20 @@ use reth_chainspec::ChainSpec; use reth_consensus::{Consensus, HeaderValidator}; use reth_db_common::init::{insert_genesis_hashes, insert_genesis_history, insert_genesis_state}; use reth_ethereum_consensus::{validate_block_post_execution, EthBeaconConsensus}; -use reth_ethereum_primitives::Block; +use reth_ethereum_primitives::{Block, TransactionSigned}; use reth_evm::{execute::Executor, ConfigureEvm}; use reth_evm_ethereum::EthEvmConfig; -use reth_primitives_traits::{RecoveredBlock, SealedBlock}; +use reth_primitives_traits::{Block as BlockTrait, RecoveredBlock, SealedBlock}; use reth_provider::{ test_utils::create_test_provider_factory_with_chain_spec, BlockWriter, DatabaseProviderFactory, ExecutionOutcome, HeaderProvider, HistoryWriter, OriginalValuesKnown, StateProofProvider, StateWriter, StaticFileProviderFactory, StaticFileSegment, StaticFileWriter, }; use reth_revm::{database::StateProviderDatabase, witness::ExecutionWitnessRecord, State}; -use reth_stateless::{validation::stateless_validation, ExecutionWitness}; +use reth_stateless::{ + trie::StatelessSparseTrie, validation::stateless_validation_with_trie, ExecutionWitness, + UncompressedPublicKey, +}; use reth_trie::{HashedPostState, KeccakKeyHasher, StateRoot}; use reth_trie_db::DatabaseStateRoot; use std::{ @@ -356,9 +359,16 @@ fn run_case( } // Now validate using the stateless client if everything else passes - for (block, execution_witness) in &program_inputs { - stateless_validation( - block.clone(), + for (recovered_block, execution_witness) in &program_inputs { + let block = recovered_block.clone().into_block(); + + // Recover the actual public keys from the transaction signatures + let public_keys = recover_signers(block.body().transactions()) + .expect("Failed to recover public keys from transaction signatures"); + + stateless_validation_with_trie::( + block, + public_keys, execution_witness.clone(), chain_spec.clone(), EthEvmConfig::new(chain_spec.clone()), @@ -413,6 +423,26 @@ fn pre_execution_checks( Ok(()) } +/// Recover public keys from transaction signatures. +fn recover_signers<'a, I>(txs: I) -> Result, Box> +where + I: IntoIterator, +{ + txs.into_iter() + .enumerate() + .map(|(i, tx)| { + tx.signature() + .recover_from_prehash(&tx.signature_hash()) + .map(|keys| { + UncompressedPublicKey( + keys.to_encoded_point(false).as_bytes().try_into().unwrap(), + ) + }) + .map_err(|e| format!("failed to recover signature for tx #{i}: {e}").into()) + }) + .collect::, _>>() +} + /// Returns whether the test at the given path should be skipped. /// /// Some tests are edge cases that cannot happen on mainnet, while others are skipped for